pencil 0.2.0 → 0.2.2
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/bin/pencil +1 -1
- data/docs/pencil_options.md +176 -0
- data/lib/config.rb +103 -0
- data/lib/config.ru +2 -0
- data/lib/dash.rb +153 -0
- data/lib/helpers.rb +197 -0
- data/lib/models/base.rb +73 -0
- data/lib/models/dashboard.rb +106 -0
- data/lib/models/graph.rb +304 -0
- data/lib/models/host.rb +82 -0
- data/lib/models.rb +3 -0
- data/lib/namespace.rb +4 -0
- data/lib/public/favicon.ico +0 -0
- data/lib/public/style.css +97 -0
- data/lib/views/cluster.erb +23 -0
- data/lib/views/dash-cluster-zoom.erb +36 -0
- data/lib/views/dash-cluster.erb +18 -0
- data/lib/views/dash-global-zoom.erb +30 -0
- data/lib/views/dash-global.erb +20 -0
- data/lib/views/global.erb +31 -0
- data/lib/views/host.erb +14 -0
- data/lib/views/layout.erb +55 -0
- data/lib/views/partials/cluster_selector.erb +6 -0
- data/lib/views/partials/cluster_switcher.erb +12 -0
- data/lib/views/partials/cookies_form.erb +12 -0
- data/lib/views/partials/dash_switcher.erb +8 -0
- data/lib/views/partials/graph_switcher.erb +8 -0
- data/lib/views/partials/hosts_selector.erb +7 -0
- data/lib/views/partials/input_boxes.erb +25 -0
- data/lib/views/partials/refresh_button.erb +8 -0
- metadata +52 -10
@@ -0,0 +1,106 @@
|
|
1
|
+
require "models/base"
|
2
|
+
require "models/graph"
|
3
|
+
require "models/host"
|
4
|
+
require "set"
|
5
|
+
|
6
|
+
module Dash::Models
|
7
|
+
class Dashboard < Base
|
8
|
+
attr_accessor :graphs
|
9
|
+
attr_accessor :graph_opts
|
10
|
+
|
11
|
+
def initialize(name, params={})
|
12
|
+
super
|
13
|
+
|
14
|
+
@graphs = []
|
15
|
+
@graph_opts = {}
|
16
|
+
params["graphs"].each do |name|
|
17
|
+
# graphs map to option hashes
|
18
|
+
if name.instance_of?(Hash)
|
19
|
+
g = Graph.find(name.keys.first) # should only be one key
|
20
|
+
@graph_opts[g] = name[name.keys.first]||{}
|
21
|
+
else
|
22
|
+
raise "Bad format for graph (must be a hash)"
|
23
|
+
end
|
24
|
+
|
25
|
+
@graphs << g if g
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def clusters
|
31
|
+
clusters = Set.new
|
32
|
+
@graphs.each { |g| clusters += get_valid_hosts(g)[1] }
|
33
|
+
clusters.sort
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_all_hosts(cluster=nil)
|
37
|
+
hosts = Set.new
|
38
|
+
clusters = Set.new
|
39
|
+
@graphs.each do |g|
|
40
|
+
h, c = get_valid_hosts(g, cluster)
|
41
|
+
hosts += h
|
42
|
+
clusters += c
|
43
|
+
end
|
44
|
+
return hosts, clusters
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_valid_hosts(graph, cluster=nil)
|
48
|
+
clusters = Set.new
|
49
|
+
if cluster
|
50
|
+
hosts = Host.find_by_cluster(cluster)
|
51
|
+
else
|
52
|
+
hosts = Host.all
|
53
|
+
hosts.each { |h| clusters << h.cluster }
|
54
|
+
end
|
55
|
+
|
56
|
+
# filter by what matches the graph definition
|
57
|
+
hosts = hosts.select { |h| h.multi_match(graph["hosts"]) }
|
58
|
+
|
59
|
+
# filter if we have a dashboard-level 'hosts' filter
|
60
|
+
if @params["hosts"]
|
61
|
+
hosts = hosts.select { |h| h.multi_match(@params["hosts"]) }
|
62
|
+
end
|
63
|
+
|
64
|
+
hosts.each { |h| clusters << h.cluster }
|
65
|
+
|
66
|
+
return hosts, clusters
|
67
|
+
end
|
68
|
+
|
69
|
+
def render_cluster_graph(graph, clusters, opts={})
|
70
|
+
# FIXME: edge case where the dash filter does not filter to a subset of
|
71
|
+
# the hosts filter
|
72
|
+
|
73
|
+
hosts = get_host_wildcards(graph)
|
74
|
+
opts[:sum] = :cluster unless opts[:zoom]
|
75
|
+
graph_url = graph.render_url(hosts.to_a, clusters, opts)
|
76
|
+
return graph_url
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_host_wildcards(graph)
|
80
|
+
return graph_opts[graph]["hosts"] || @params["hosts"] || graph["hosts"]
|
81
|
+
end
|
82
|
+
|
83
|
+
def render_global_graph(graph, opts={})
|
84
|
+
hosts = get_host_wildcards(graph)
|
85
|
+
_, clusters = get_valid_hosts(graph)
|
86
|
+
|
87
|
+
next_url = ""
|
88
|
+
type = opts[:zoom] ? :cluster : :global
|
89
|
+
options = opts.merge({:sum => type})
|
90
|
+
graph_url = graph.render_url(hosts, clusters, options)
|
91
|
+
return graph_url
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.find_by_graph(graph)
|
95
|
+
ret = []
|
96
|
+
Dashboard.each do |name, dash|
|
97
|
+
|
98
|
+
if dash["graphs"].map { |x| x.keys.first }.member?(graph.name)
|
99
|
+
ret << dash
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
return ret
|
104
|
+
end
|
105
|
+
end # Dash::Models::Dashboard
|
106
|
+
end # Dash::Models
|
data/lib/models/graph.rb
ADDED
@@ -0,0 +1,304 @@
|
|
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", "ratio"
|
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
|
+
str #color is handled elsewhere
|
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
|
+
# later on
|
87
|
+
def handle_metric(name, opts, inner=false)
|
88
|
+
ret = name.dup
|
89
|
+
if inner
|
90
|
+
@params.each do |k, v|
|
91
|
+
ret = translate(k, ret, v, true)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
(opts||{}).each do |k, v|
|
95
|
+
#puts "#{k} => #{v}"
|
96
|
+
ret = translate(k, ret, v)
|
97
|
+
end
|
98
|
+
ret
|
99
|
+
end
|
100
|
+
|
101
|
+
def render_url(hosts, clusters, opts={})
|
102
|
+
opts = {
|
103
|
+
:sum => nil,
|
104
|
+
:title => @params["title"],
|
105
|
+
}.merge(opts)
|
106
|
+
|
107
|
+
if ! [:global, :cluster, nil].member?(opts[:sum])
|
108
|
+
raise ArgumentError, "render graph #{name}: invalid :sum - #{opts[:sum]}"
|
109
|
+
end
|
110
|
+
|
111
|
+
sym_hash = {}
|
112
|
+
(opts[:dynamic_url_opts]||[]).each do |k,v|
|
113
|
+
sym_hash[k.to_sym] = v
|
114
|
+
end
|
115
|
+
|
116
|
+
# fixme key checking may be necessary
|
117
|
+
url_opts = {
|
118
|
+
:title => opts[:title],
|
119
|
+
}.merge(@params[:url_opts]).merge(sym_hash)
|
120
|
+
|
121
|
+
url_opts[:from] = url_opts.delete(:stime) || ""
|
122
|
+
url_opts[:until] = url_opts.delete(:etime) || ""
|
123
|
+
url_opts.delete(:start)
|
124
|
+
url_opts.delete(:duration)
|
125
|
+
|
126
|
+
graphite_opts = [ "vtitle", "yMin", "yMax", "lineWidth", "areaMode",
|
127
|
+
"template", "lineMode", "bgcolor", "graphOnly", "hideAxes", "hideGrid",
|
128
|
+
"hideLegend", "fgcolor", "fontSize", "fontName", "fontItalic",
|
129
|
+
"fontBold" ]
|
130
|
+
|
131
|
+
@params.each do |k, v|
|
132
|
+
if graphite_opts.member?(k)
|
133
|
+
url_opts[k.to_sym] = v
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
target = []
|
138
|
+
colors = []
|
139
|
+
#fixme code duplication
|
140
|
+
if opts[:sum] == :global
|
141
|
+
@params["targets"].each do |stat_name, opts|
|
142
|
+
z = opts.dup
|
143
|
+
# opts['key'] ||= stat_name
|
144
|
+
z[:key] ||= stat_name
|
145
|
+
#######################
|
146
|
+
if stat_name.instance_of?(Array)
|
147
|
+
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)
|
151
|
+
end.join(",")
|
152
|
+
else
|
153
|
+
metric = "#{stat_name}.{#{clusters.to_a.join(',')}}" +
|
154
|
+
".{#{hosts.to_a.join(',')}}"
|
155
|
+
metric = handle_metric(metric, {}, true)
|
156
|
+
end
|
157
|
+
#######################
|
158
|
+
z[:key] = "global #{z[:key]}"
|
159
|
+
target << handle_metric("sumSeries(#{metric})", z)
|
160
|
+
colors << next_color(colors, z[:color])
|
161
|
+
end # @params["targets"].each
|
162
|
+
elsif opts[:sum] == :cluster # one line per cluster/metric
|
163
|
+
clusters.each do |cluster|
|
164
|
+
@params["targets"].each do |stat_name, opts|
|
165
|
+
z = opts.dup
|
166
|
+
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)
|
172
|
+
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
|
+
|
180
|
+
z[:key] = "#{cluster} #{z[:key]}"
|
181
|
+
target << handle_metric("sumSeries(#{metrics.join(',')})", z)
|
182
|
+
colors << next_color(colors, z[:color])
|
183
|
+
end # metrics.each
|
184
|
+
end # clusters.each
|
185
|
+
else # one line per {metric,host,colo}
|
186
|
+
@params["targets"].each do |stat_name, opts|
|
187
|
+
clusters.each do |cluster|
|
188
|
+
hosts.each do |host|
|
189
|
+
label = "#{host} #{opts[:key]}"
|
190
|
+
#################
|
191
|
+
if stat_name.instance_of?(Array)
|
192
|
+
metric = stat_name.map do |m|
|
193
|
+
mm = compose_metric(m.keys.first, cluster, host)
|
194
|
+
handle_metric(mm, m[m.keys.first], true)
|
195
|
+
end.join(",")
|
196
|
+
else
|
197
|
+
metric = handle_metric(compose_metric(stat_name, cluster, host), {}, true)
|
198
|
+
end
|
199
|
+
#################
|
200
|
+
|
201
|
+
if label =~ /\*/
|
202
|
+
# for this particular type of graph, don't display a legend
|
203
|
+
url_opts[:hideLegend] = true
|
204
|
+
z = opts.dup
|
205
|
+
# fixme proper labeling... maybe
|
206
|
+
# With wildcards let graphite construct the legend (or not).
|
207
|
+
# Since we're handling wildcards we don't know how many
|
208
|
+
# hosts will match, so just put in the default color list.
|
209
|
+
# technically we do know, so this can be fixed
|
210
|
+
z.delete(:key)
|
211
|
+
target << handle_metric(metric, z)
|
212
|
+
colors.concat(@params[:default_colors]) if colors.empty?
|
213
|
+
else
|
214
|
+
z = opts.dup
|
215
|
+
z[:key] = "#{host}/#{cluster} #{opts[:key]}"
|
216
|
+
target << handle_metric(metric, z)
|
217
|
+
colors << next_color(colors, opts[:color])
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end # @params["targets"].each
|
222
|
+
end # if opts[:sum]
|
223
|
+
|
224
|
+
url_opts[:target] = target
|
225
|
+
url_opts[:colorList] = colors.join(",")
|
226
|
+
|
227
|
+
url = URI.join(@params[:graphite_url], "/render/?").to_s
|
228
|
+
url_parts = []
|
229
|
+
url_opts.each do |k, v|
|
230
|
+
[v].flatten.each do |v|
|
231
|
+
url_parts << "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
url += url_parts.join("&")
|
235
|
+
return url
|
236
|
+
end
|
237
|
+
|
238
|
+
# return an array of all metrics matching the specifications in
|
239
|
+
# @params["targets"]
|
240
|
+
# metrics are arrays of fields (once delimited by periods)
|
241
|
+
def expand
|
242
|
+
url = URI.join(@params[:graphite_url], "/metrics/expand/?query=").to_s
|
243
|
+
metrics = []
|
244
|
+
|
245
|
+
@params["targets"].each do |metric|
|
246
|
+
if metric.first.instance_of?(Array)
|
247
|
+
metric.first.each do |m|
|
248
|
+
composed = compose_metric(m.first.first, "*", "*")
|
249
|
+
query = open("#{url}#{composed}").read
|
250
|
+
metrics << JSON.parse(query)["results"]
|
251
|
+
end
|
252
|
+
else
|
253
|
+
composed = compose_metric(metric.first.first, "*", "*")
|
254
|
+
query = open("#{url}#{composed}").read
|
255
|
+
metrics << JSON.parse(query)["results"]
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
return metrics.flatten.map { |x| x.split(".") }
|
260
|
+
end
|
261
|
+
|
262
|
+
def hosts_clusters
|
263
|
+
metrics = expand
|
264
|
+
clusters = Set.new
|
265
|
+
|
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] }))
|
269
|
+
end.uniq
|
270
|
+
|
271
|
+
# filter by what matches the graph definition
|
272
|
+
hosts = hosts.select { |h| h.multi_match(@params["hosts"]) }
|
273
|
+
hosts.each { |h| clusters << h.cluster }
|
274
|
+
|
275
|
+
return hosts, clusters
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
def next_color(colors, preferred_color=nil)
|
280
|
+
default_colors = @params[:default_colors].clone
|
281
|
+
|
282
|
+
if preferred_color and !colors.member?(preferred_color)
|
283
|
+
return preferred_color
|
284
|
+
end
|
285
|
+
|
286
|
+
if preferred_color and ! default_colors.member?(preferred_color)
|
287
|
+
default_colors << preferred_color
|
288
|
+
end
|
289
|
+
|
290
|
+
weights = Hash.new { |h, k| h[k] = 0 }
|
291
|
+
colors.each do |c|
|
292
|
+
weights[c] += 1
|
293
|
+
end
|
294
|
+
|
295
|
+
i = 0
|
296
|
+
loop do
|
297
|
+
default_colors.each do |c|
|
298
|
+
return c if weights[c] == i
|
299
|
+
end
|
300
|
+
i += 1
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end # Dash::Models::Graph
|
304
|
+
end # Dash::Models
|
data/lib/models/host.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "models/base"
|
2
|
+
require "models/graph"
|
3
|
+
|
4
|
+
module Dash::Models
|
5
|
+
class Host < Base
|
6
|
+
attr_accessor :graphs
|
7
|
+
|
8
|
+
def initialize(name, params={})
|
9
|
+
super
|
10
|
+
|
11
|
+
@graphs = []
|
12
|
+
Graph.each do |graph_name, graph|
|
13
|
+
graph["hosts"].each do |h|
|
14
|
+
if match(h)
|
15
|
+
@graphs << graph
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end # graph["hosts"].each
|
19
|
+
end # Graph.each
|
20
|
+
end
|
21
|
+
|
22
|
+
def cluster
|
23
|
+
return @params["cluster"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def key
|
27
|
+
"#{@cluster}#{@name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def eql?(other)
|
31
|
+
key == other.key
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
key == other.key
|
36
|
+
end
|
37
|
+
|
38
|
+
def <=>(other)
|
39
|
+
if @params[:host_sort] == "builtin"
|
40
|
+
return key <=> other.key
|
41
|
+
elsif @params[:host_sort] == "numeric"
|
42
|
+
regex = /\d+/
|
43
|
+
match = @name.match(regex)
|
44
|
+
match2 = other.name.match(regex)
|
45
|
+
if match.pre_match != match2.pre_match
|
46
|
+
return match.pre_match <=> match2.pre_match
|
47
|
+
else
|
48
|
+
return match[0].to_i <=> match2[0].to_i
|
49
|
+
end
|
50
|
+
else
|
51
|
+
# http://www.bofh.org.uk/2007/12/16/comprehensible-sorting-in-ruby
|
52
|
+
sensible = lambda do |k|
|
53
|
+
k.to_s.split(
|
54
|
+
/((?:(?:^|\s)[-+])?(?:\.\d+|\d+(?:\.\d+?(?:[eE]\d+)?(?:$|(?![eE\.])))?))/ms
|
55
|
+
).map { |v| Float(v) rescue v.downcase }
|
56
|
+
end
|
57
|
+
return sensible.call(self) <=> sensible.call(other)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def hash
|
62
|
+
key.hash
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.find_by_name_and_cluster(name, cluster)
|
66
|
+
Host.each do |host_name, host|
|
67
|
+
next unless host_name = name
|
68
|
+
return host if host.cluster == cluster
|
69
|
+
end
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.find_by_cluster(cluster)
|
74
|
+
ret = []
|
75
|
+
Host.each do |name, host|
|
76
|
+
ret << host if host.cluster == cluster
|
77
|
+
end
|
78
|
+
return ret
|
79
|
+
end
|
80
|
+
|
81
|
+
end # Dash::Models::Host
|
82
|
+
end # Dash::Models
|
data/lib/models.rb
ADDED
data/lib/namespace.rb
ADDED
Binary file
|
@@ -0,0 +1,97 @@
|
|
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
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<h2>List of dashboards for <%= cluster_selector %></h2>
|
2
|
+
|
3
|
+
<% hosts = Host.all.select { |h| h.cluster == @cluster }
|
4
|
+
boards = @dashboards.select { |d| d.clusters.member?(@cluster) }
|
5
|
+
seen_hosts = Set.new %>
|
6
|
+
|
7
|
+
<% boards.each do |b| %>
|
8
|
+
<h2><a href="/dash/<%= "#{@cluster}/#{append_query_string(b.name)}" %>"><%= b.name %></a>
|
9
|
+
<% dash_hosts = b.get_all_hosts(@cluster)[0]
|
10
|
+
seen_hosts += dash_hosts %>
|
11
|
+
<%= hosts_selector(dash_hosts) %>
|
12
|
+
</h2>
|
13
|
+
<%= b.graphs.collect do |g|
|
14
|
+
href = append_query_string("/dash/#{@cluster}/#{b.name}/#{g}")
|
15
|
+
"<li><a href=\"#{href}\">#{g}</a><br></li>"
|
16
|
+
end %>
|
17
|
+
<br>
|
18
|
+
<% end %>
|
19
|
+
<h3>Other Hosts (not associated with a dashboard)</h3>
|
20
|
+
<%= (hosts.to_set - seen_hosts).sort.collect do |h|
|
21
|
+
href = append_query_string("/host/#{@cluster}/#{h}")
|
22
|
+
"<li><a href=\"#{href}\">#{h}</a></li>"
|
23
|
+
end %>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<% hosts = @dash.get_valid_hosts(@zoom, @cluster)[0].sort %>
|
2
|
+
|
3
|
+
<%= cluster_switcher %>
|
4
|
+
<%= graph_switcher %>
|
5
|
+
<%= graph_uplink %>
|
6
|
+
<br>
|
7
|
+
<br>
|
8
|
+
<a href="#by_host">by_host</a>
|
9
|
+
<%= hosts.collect { |h| "<a href=\"##{h}\">#{h}</a>" }.join(" ") %>
|
10
|
+
|
11
|
+
<div class="graphsection" style="width:<%= @zoom.width(merge_opts) %>;">
|
12
|
+
<span class="graphtitle"><%= @zoom.name %> / <%= @cluster %> :: summary</span>
|
13
|
+
<div class="graph">
|
14
|
+
<img src="<%= @dash.render_cluster_graph(@zoom, @cluster, :dynamic_url_opts => merge_opts) %>">
|
15
|
+
</div>
|
16
|
+
</div>
|
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
|
+
<% hosts.each do |host| %>
|
27
|
+
<div class="graphsection" style="width:<%= @zoom.width(merge_opts) %>;">
|
28
|
+
<a name="<%= host %>">
|
29
|
+
<% image_url, zoom_url = cluster_zoom_graph(@zoom, @cluster, host, "#{@zoom.name} / #{@cluster} / #{host}") %>
|
30
|
+
<span class="graphtitle"><%= @zoom.name %> / <%= @cluster %> / <%= host %></span>
|
31
|
+
<span class="dashlinks">(<a href="<%= zoom_url %>">host</a>)</span>
|
32
|
+
<div class="graph">
|
33
|
+
<a href="<%= zoom_url %>"><img src="<%= image_url %>"></a>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= cluster_switcher %>
|
2
|
+
<%= dash_switcher %>
|
3
|
+
<%= dash_uplink %>
|
4
|
+
<br>
|
5
|
+
<br>
|
6
|
+
<%= @dash.graphs.collect { |g| "<a href=\"##{g.name}\">#{g.name}</a>" }.join(" ") %>
|
7
|
+
|
8
|
+
<% @dash.graphs.each do |g| %>
|
9
|
+
<div class="graphsection" style="width:<%= g.width(merge_opts) %>;">
|
10
|
+
<a name="<%= g.name %>">
|
11
|
+
<% image_url, zoom_url = cluster_graph(g, @cluster, "#{g.name} / #{@cluster}") %>
|
12
|
+
<span class="graphtitle"><%= g.name %></span>
|
13
|
+
<span class="dashlinks">(<a href="<%= zoom_url %>">zoom</a>)</span>
|
14
|
+
<div class="graph">
|
15
|
+
<a href="<%= zoom_url %>"><img src="<%= image_url %>"></a>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
<% end %>
|