pencil 0.2.10 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/pencil +2 -6
- data/docs/pencil_options.md +3 -1
- data/lib/{config.rb → pencil/config.rb} +29 -9
- data/lib/pencil/config.ru +2 -0
- data/lib/pencil/helpers.rb +211 -0
- data/lib/pencil/models/base.rb +78 -0
- data/lib/pencil/models/dashboard.rb +124 -0
- data/lib/pencil/models/graph.rb +394 -0
- data/lib/pencil/models/host.rb +89 -0
- data/lib/pencil/models.rb +3 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_flat_30_cccccc_40x100.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_flat_50_5c5c5c_40x100.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_glass_20_333333_1x400.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_glass_40_000000_1x400.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_glass_40_ffc73d_1x400.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_gloss-wave_25_333333_500x100.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_highlight-soft_15_000000_1x100.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_highlight-soft_80_eeeeee_1x100.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_inset-soft_30_ff4e0a_1x100.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_222222_256x240.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_4b8e0b_256x240.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_a83300_256x240.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_cccccc_256x240.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_ffffff_256x240.png +0 -0
- data/lib/{public → pencil/public}/css/jquery_themes/pencil/jquery-ui-1.8.16.custom.css +0 -0
- data/lib/{public → pencil/public}/css/thedoc.css +1 -0
- data/lib/{public → pencil/public}/favicon.ico +0 -0
- data/lib/{public → pencil/public}/js/cufon.js +0 -0
- data/lib/{public → pencil/public}/js/gotham.font.js +0 -0
- data/lib/{public → pencil/public}/js/jquery-1.6.2.min.js +0 -0
- data/lib/{public → pencil/public}/js/jquery-ui-1.8.16.custom.min.js +0 -0
- data/lib/{public → pencil/public}/js/pencil.js +0 -0
- data/lib/{public → pencil/public}/js/product-design.font.js +0 -0
- data/lib/pencil/public/style.css +307 -0
- data/lib/{rubyfixes.rb → pencil/rubyfixes.rb} +0 -0
- data/lib/pencil/version.rb +3 -0
- data/lib/{views → pencil/views}/cluster.erb +1 -1
- data/lib/{views → pencil/views}/dash-cluster-zoom.erb +2 -2
- data/lib/{views → pencil/views}/dash-cluster.erb +1 -1
- data/lib/{views → pencil/views}/dash-global-zoom.erb +2 -2
- data/lib/{views → pencil/views}/dash-global.erb +1 -1
- data/lib/{views → pencil/views}/global.erb +1 -1
- data/lib/{views → pencil/views}/host.erb +1 -1
- data/lib/{views → pencil/views}/layout.erb +2 -2
- data/lib/{views → pencil/views}/partials/cluster_selector.erb +0 -0
- data/lib/{views → pencil/views}/partials/cluster_switcher.erb +0 -0
- data/lib/{views → pencil/views}/partials/dash_switcher.erb +0 -0
- data/lib/{views → pencil/views}/partials/graph_switcher.erb +0 -0
- data/lib/{views → pencil/views}/partials/hosts_selector.erb +0 -0
- data/lib/{views → pencil/views}/partials/input_boxes.erb +0 -0
- data/lib/{views → pencil/views}/partials/shortcuts.erb +0 -0
- data/lib/{dash.rb → pencil.rb} +14 -17
- metadata +57 -58
- data/VERSION.rb +0 -1
- data/lib/config.ru +0 -2
- data/lib/helpers.rb +0 -209
- data/lib/models/base.rb +0 -73
- data/lib/models/dashboard.rb +0 -111
- data/lib/models/graph.rb +0 -367
- data/lib/models/host.rb +0 -87
- data/lib/models.rb +0 -3
- data/lib/namespace.rb +0 -4
- data/lib/public/style.css +0 -97
data/bin/pencil
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
require 'pencil'
|
3
3
|
|
4
|
-
|
5
|
-
require "dash"
|
4
|
+
Pencil::App.run!
|
6
5
|
|
7
|
-
STDOUT.puts "pencil v#{PENCIL_VERSION} on port #{Dash::App.port}, PID #{$$}"
|
8
|
-
|
9
|
-
Rack::Handler::Mongrel.run(Dash::App, :Port => Dash::App.port)
|
data/docs/pencil_options.md
CHANGED
@@ -203,7 +203,9 @@ Most of these options take a single argument.
|
|
203
203
|
* threshold
|
204
204
|
* color
|
205
205
|
|
206
|
-
Note: key
|
206
|
+
Note: key and color are applied after all other options are applied.
|
207
|
+
|
208
|
+
key is interpreted differently from the other options, which are more
|
207
209
|
simply translated.
|
208
210
|
|
209
211
|
color's interpretation depends on whether :use_color is set.
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require "models"
|
1
|
+
require 'map'
|
2
|
+
require "pencil/models"
|
3
3
|
|
4
|
-
module
|
4
|
+
module Pencil
|
5
5
|
class Config
|
6
|
-
include
|
6
|
+
include Pencil::Models
|
7
7
|
|
8
8
|
attr_reader :dashboards
|
9
9
|
attr_reader :graphs
|
@@ -32,11 +32,31 @@ module Dash
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def reload!
|
35
|
-
configs = Dir.glob("#{@confdir}
|
36
|
-
configs.each
|
35
|
+
configs = Dir.glob("#{@confdir}/**/*.y{a,}ml")
|
36
|
+
configs.each do |c|
|
37
|
+
yml = YAML.load(File.read(c))
|
38
|
+
next unless yml
|
39
|
+
@rawconfig[:config] = yml[:config] if yml[:config]
|
40
|
+
a = @rawconfig[:dashboards]
|
41
|
+
b = yml[:dashboards]
|
42
|
+
c = @rawconfig[:graphs]
|
43
|
+
d = yml[:graphs]
|
44
|
+
|
45
|
+
if a && b
|
46
|
+
a.merge!(b)
|
47
|
+
elsif b
|
48
|
+
@rawconfig[:dashboards] = b
|
49
|
+
end
|
50
|
+
if c && d
|
51
|
+
c.merge!(d)
|
52
|
+
elsif d
|
53
|
+
@rawconfig[:graphs] = d
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@rawconfig = Map(@rawconfig)
|
37
57
|
|
38
58
|
[:graphs, :dashboards, :config].each do |c|
|
39
|
-
if not @rawconfig[c]
|
59
|
+
if not @rawconfig[c.to_s]
|
40
60
|
raise "Missing config name '#{c.to_s}'"
|
41
61
|
end
|
42
62
|
end
|
@@ -99,5 +119,5 @@ module Dash
|
|
99
119
|
@dashboards, @graphs = dashboards_new, graphs_new
|
100
120
|
@hosts, @clusters = hosts_new, clusters_new
|
101
121
|
end
|
102
|
-
end #
|
103
|
-
end #
|
122
|
+
end # Pencil::Config
|
123
|
+
end # Pencil
|
@@ -0,0 +1,211 @@
|
|
1
|
+
module Pencil
|
2
|
+
module Helpers
|
3
|
+
include Pencil::Models
|
4
|
+
|
5
|
+
@@prefs = [["Start Time", "start"],
|
6
|
+
["Duration", "duration"],
|
7
|
+
["Width", "width"],
|
8
|
+
["Height", "height"]]
|
9
|
+
|
10
|
+
# convert keys to symbols before lookup
|
11
|
+
def param_lookup(name)
|
12
|
+
sym_hash = {}
|
13
|
+
session.each { |k,v| sym_hash[k.to_sym] = v unless v.empty? }
|
14
|
+
params.each { |k,v| sym_hash[k.to_sym] = v unless v.empty? }
|
15
|
+
settings.config.global_config[:url_opts].merge(sym_hash)[name.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
def cluster_graph(g, cluster, title="wtf")
|
19
|
+
image_url = \
|
20
|
+
@dash.render_cluster_graph(g, cluster,
|
21
|
+
:title => title,
|
22
|
+
:dynamic_url_opts => merge_opts)
|
23
|
+
zoom_url = cluster_graph_link(@dash, g, cluster)
|
24
|
+
return image_url, zoom_url
|
25
|
+
end
|
26
|
+
|
27
|
+
def cluster_graph_link(dash, g, cluster)
|
28
|
+
link = dash.graph_opts[g]["click"] ||
|
29
|
+
"/dash/#{cluster}/#{dash.name}/#{g.name}"
|
30
|
+
return append_query_string(link)
|
31
|
+
end
|
32
|
+
|
33
|
+
def cluster_zoom_graph(g, cluster, host, title)
|
34
|
+
image_url = g.render_url([host.name], [cluster], :title => title,
|
35
|
+
:dynamic_url_opts => merge_opts)
|
36
|
+
zoom_url = cluster_zoom_link(cluster, host)
|
37
|
+
return image_url, zoom_url
|
38
|
+
end
|
39
|
+
|
40
|
+
def cluster_zoom_link(cluster, host)
|
41
|
+
return append_query_string("/host/#{cluster}/#{host}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def suggest_cluster_links(clusters, g)
|
45
|
+
links = []
|
46
|
+
clusters.each do |c|
|
47
|
+
href = append_query_string("/dash/#{c}/#{params[:dashboard]}/#{g.name}")
|
48
|
+
links << "<a href=\"#{href}\">#{c}</a>"
|
49
|
+
end
|
50
|
+
return "zoom (" + links.join(", ") + ")"
|
51
|
+
end
|
52
|
+
|
53
|
+
def suggest_dashboards_links(host, graph)
|
54
|
+
suggested = suggest_dashboards(host, graph)
|
55
|
+
return "" if suggested.length == 0
|
56
|
+
|
57
|
+
links = []
|
58
|
+
suggested.each do |d|
|
59
|
+
links << "<a href=\"/dash/#{host.cluster}/#{append_query_string(d)}\">" +
|
60
|
+
"#{d}</a>"
|
61
|
+
end
|
62
|
+
return "(" + links.join(", ") + ")"
|
63
|
+
end
|
64
|
+
|
65
|
+
# it's mildly annoying that when this set is empty there're no uplinks
|
66
|
+
# consider adding a link up to the cluster (which is best we can do)
|
67
|
+
def suggest_dashboards(host, graph)
|
68
|
+
ret = Set.new
|
69
|
+
|
70
|
+
host.graphs.each do |g|
|
71
|
+
Dashboard.find_by_graph(g).each do |d|
|
72
|
+
valid, _ = d.get_valid_hosts(g, host['cluster'])
|
73
|
+
ret << d.name if valid.member?(host)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
return ret
|
78
|
+
end
|
79
|
+
|
80
|
+
# generate the input box fields, filled in to current parameters if specified
|
81
|
+
def input_boxes
|
82
|
+
@prefs = @@prefs
|
83
|
+
erb :'partials/input_boxes', :layout => false
|
84
|
+
end
|
85
|
+
|
86
|
+
def dash_link(dash, cluster)
|
87
|
+
return append_query_string("/dash/#{cluster}/#{dash.name}")
|
88
|
+
end
|
89
|
+
|
90
|
+
def cluster_link(cluster)
|
91
|
+
return append_query_string("/dash/#{cluster}")
|
92
|
+
end
|
93
|
+
|
94
|
+
# fixme this isn't used anymore, but should
|
95
|
+
def css_url
|
96
|
+
style = File.join(settings.root, "public/style.css")
|
97
|
+
mtime = File.mtime(style).to_i.to_s
|
98
|
+
return \
|
99
|
+
%Q[<link href="/style.css?#{mtime}" rel="stylesheet" type="text/css">]
|
100
|
+
end
|
101
|
+
|
102
|
+
def refresh
|
103
|
+
if settings.config.global_config[:refresh_rate] != false && nowish
|
104
|
+
rate = settings.config.global_config[:refresh_rate] || 60
|
105
|
+
return %Q[<meta http-equiv="refresh" content="#{rate}">]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def hosts_selector(hosts, print_clusters=false)
|
110
|
+
@print_clusters = print_clusters
|
111
|
+
@hosts = hosts
|
112
|
+
erb :'partials/hosts_selector', :layout => false
|
113
|
+
end
|
114
|
+
|
115
|
+
def append_query_string(str)
|
116
|
+
v = str.dup
|
117
|
+
(v << "?#{request.query_string}") unless request.query_string.empty?
|
118
|
+
return v
|
119
|
+
end
|
120
|
+
|
121
|
+
def merge_opts
|
122
|
+
static_opts = ["cluster", "dashboard", "zoom", "host", "session_id"]
|
123
|
+
opts = params.dup
|
124
|
+
session.merge(opts).delete_if { |k,v| static_opts.member?(k) || v.empty? }
|
125
|
+
end
|
126
|
+
|
127
|
+
def shortcuts(str)
|
128
|
+
@str = str
|
129
|
+
erb :'partials/shortcuts', :layout => false
|
130
|
+
end
|
131
|
+
def cluster_switcher(clusters)
|
132
|
+
@clusters = clusters
|
133
|
+
erb :'partials/cluster_switcher', :layout => false
|
134
|
+
end
|
135
|
+
|
136
|
+
def dash_switcher
|
137
|
+
erb :'partials/dash_switcher', :layout => false
|
138
|
+
end
|
139
|
+
|
140
|
+
def graph_switcher
|
141
|
+
erb :'partials/graph_switcher', :layout => false
|
142
|
+
end
|
143
|
+
|
144
|
+
def cluster_selector
|
145
|
+
@clusters = settings.config.clusters.sort + ["global"]
|
146
|
+
erb :'partials/cluster_selector', :layout => false
|
147
|
+
end
|
148
|
+
|
149
|
+
def host_uplink
|
150
|
+
link = "/dash/#{append_query_string(@host.cluster)}"
|
151
|
+
"zoom out: <a href=\"#{link}\">#{@host.cluster}</a>"
|
152
|
+
end
|
153
|
+
|
154
|
+
def graph_uplink
|
155
|
+
link = append_query_string(request.path.split("/")[0..-2].join("/"))
|
156
|
+
"zoom out: <a href=\"#{link}\">#{@dash}</a>"
|
157
|
+
end
|
158
|
+
|
159
|
+
def dash_uplink
|
160
|
+
link = append_query_string(request.path.split("/")[0..-2].join("/"))
|
161
|
+
"zoom out: <a href=\"#{link}\">#{@params[:cluster]}</a>"
|
162
|
+
end
|
163
|
+
|
164
|
+
# fixme this is a hack
|
165
|
+
def header(str)
|
166
|
+
<<-FOO
|
167
|
+
<div class="path_container">
|
168
|
+
<h2>#{@title}</h2>
|
169
|
+
<span class="zoom">
|
170
|
+
#{str}
|
171
|
+
</span>
|
172
|
+
</div>
|
173
|
+
<div id="timeslice_container">
|
174
|
+
<h3 id="timeslice">#{range_string}</h3>
|
175
|
+
#{permalink unless @no_graphs}
|
176
|
+
</div>
|
177
|
+
FOO
|
178
|
+
end
|
179
|
+
|
180
|
+
def nowish
|
181
|
+
if settings.config.global_config[:now_threshold] == false
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
threshold = settings.config.global_config[:now_threshold] || 300
|
185
|
+
return @request_time.to_i - @etime.to_i < threshold
|
186
|
+
end
|
187
|
+
|
188
|
+
def range_string
|
189
|
+
format = settings.config.global_config[:date_format] || "%X %x"
|
190
|
+
if @stime && @etime
|
191
|
+
if nowish
|
192
|
+
"timeslice: from #{@stime.strftime(format)}"
|
193
|
+
else
|
194
|
+
"timeslice: #{@stime.strftime(format)} - #{@etime.strftime(format)}"
|
195
|
+
end
|
196
|
+
else
|
197
|
+
"invalid time range"
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
def permalink
|
203
|
+
return "" unless @stime && @duration
|
204
|
+
format = "%F %T" # chronic REALLY understands this
|
205
|
+
url = request.path + "?"
|
206
|
+
url << "&start=#{@stime.strftime(format)}"
|
207
|
+
url << "&duration=#{ChronicDuration.output(@duration)}"
|
208
|
+
"<a id=\"permalink\" href=\"#{url}\">permalink</a>"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Pencil
|
2
|
+
module Models
|
3
|
+
class Base
|
4
|
+
@@objects = Hash.new { |h, k| h[k] = Hash.new }
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(name, params={})
|
8
|
+
@name = name
|
9
|
+
@match_name = name
|
10
|
+
@params = params
|
11
|
+
@@objects[self.class.to_s][name] = self
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.find(name)
|
15
|
+
return @@objects[self.name][name] rescue []
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.each(&block)
|
19
|
+
h = @@objects[self.name] rescue {}
|
20
|
+
h.each { |k, v| yield(k, v) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
return @@objects[self.name].values
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
return @params[key] rescue []
|
29
|
+
end
|
30
|
+
|
31
|
+
def match(glob)
|
32
|
+
return true if glob == '*'
|
33
|
+
# convert glob to a regular expression
|
34
|
+
glob_re = /^#{Regexp.escape(glob).gsub('\*', '.*').gsub('\#', '\d+')}$/
|
35
|
+
return @match_name.match(glob_re)
|
36
|
+
end
|
37
|
+
|
38
|
+
def multi_match(globs)
|
39
|
+
ret = false
|
40
|
+
|
41
|
+
globs.each do |glob|
|
42
|
+
ret = match(glob)
|
43
|
+
break if ret
|
44
|
+
end
|
45
|
+
|
46
|
+
return ret
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
return @name
|
51
|
+
end
|
52
|
+
|
53
|
+
def <=>(other)
|
54
|
+
return to_s <=> other.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_params(hash)
|
58
|
+
@params.merge!(hash)
|
59
|
+
end
|
60
|
+
|
61
|
+
# compose a metric using a :metric_format
|
62
|
+
# format string with %c for metric, %c for cluster, and %h for host
|
63
|
+
def compose_metric (m, c, h)
|
64
|
+
@params[:metric_format].dup.gsub("%m", m).gsub("%c", c).gsub("%h", h)
|
65
|
+
end
|
66
|
+
|
67
|
+
# used to make sure partial metrics are not considered
|
68
|
+
# e.g. foo.bar.baz.colo1.host1 but not
|
69
|
+
# foo.bar.baz.subkey.subkey2[.colo1.host1]
|
70
|
+
# where the latter is returned by the graphite expand api but not as a full
|
71
|
+
# metric
|
72
|
+
# essentially equivalent to matching like /[:METRIC:]$/
|
73
|
+
def compose_metric2 (m, c, h)
|
74
|
+
compose_metric(m, c, h) + ".*"
|
75
|
+
end
|
76
|
+
end # Pencil::Models::Base
|
77
|
+
end
|
78
|
+
end # Pencil::Models
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "pencil/models/base"
|
2
|
+
require "pencil/models/graph"
|
3
|
+
require "pencil/models/host"
|
4
|
+
require "set"
|
5
|
+
|
6
|
+
module Pencil
|
7
|
+
module Models
|
8
|
+
class Dashboard < Base
|
9
|
+
attr_accessor :graphs
|
10
|
+
attr_accessor :graph_opts
|
11
|
+
|
12
|
+
def initialize(name, params={})
|
13
|
+
super
|
14
|
+
|
15
|
+
@graphs = []
|
16
|
+
@graph_opts = {}
|
17
|
+
params["graphs"].each do |name|
|
18
|
+
# graphs map to option hashes
|
19
|
+
if name.respond_to?(:keys) # could be YAML::Omap
|
20
|
+
g = Graph.find(name.keys.first) # should only be one key
|
21
|
+
@graph_opts[g] = name[name.keys.first]||{}
|
22
|
+
else
|
23
|
+
raise "Bad format for graph (must be a hash-y; #{name.class}:#{name.inspect} is not)"
|
24
|
+
end
|
25
|
+
|
26
|
+
@graphs << g if g
|
27
|
+
end
|
28
|
+
|
29
|
+
@valid_hosts_table = {} # cache calls to get_valid_hosts
|
30
|
+
end
|
31
|
+
|
32
|
+
def clusters
|
33
|
+
clusters = Set.new
|
34
|
+
@graphs.each { |g| clusters += get_valid_hosts(g)[1] }
|
35
|
+
clusters.sort
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_all_hosts(cluster=nil)
|
39
|
+
hosts = Set.new
|
40
|
+
clusters = Set.new
|
41
|
+
@graphs.each do |g|
|
42
|
+
h, c = get_valid_hosts(g, cluster)
|
43
|
+
hosts += h
|
44
|
+
clusters += c
|
45
|
+
end
|
46
|
+
return hosts, clusters
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_valid_hosts(graph, cluster=nil)
|
50
|
+
if @valid_hosts_table[[graph, cluster]]
|
51
|
+
return @valid_hosts_table[[graph, cluster]]
|
52
|
+
end
|
53
|
+
|
54
|
+
clusters = Set.new
|
55
|
+
if cluster
|
56
|
+
hosts = Host.find_by_cluster(cluster)
|
57
|
+
else
|
58
|
+
hosts = Host.all
|
59
|
+
end
|
60
|
+
|
61
|
+
# filter as:
|
62
|
+
# - the dashboard graph hosts definition
|
63
|
+
# - the dashboard hosts definition
|
64
|
+
# - the graph hosts definition
|
65
|
+
# this is new behavior: before the filters were additive
|
66
|
+
filter = graph_opts[graph]["hosts"] || @params["hosts"] || graph["hosts"]
|
67
|
+
if filter
|
68
|
+
hosts = hosts.select { |h| h.multi_match(filter) }
|
69
|
+
end
|
70
|
+
|
71
|
+
hosts.each { |h| clusters << h.cluster }
|
72
|
+
|
73
|
+
@valid_hosts_table[[graph, cluster]] = [hosts, clusters]
|
74
|
+
return hosts, clusters
|
75
|
+
end
|
76
|
+
|
77
|
+
def render_cluster_graph(graph, clusters, opts={})
|
78
|
+
# FIXME: edge case where the dash filter does not filter to a subset of
|
79
|
+
# the hosts filter
|
80
|
+
|
81
|
+
hosts = get_host_wildcards(graph)
|
82
|
+
|
83
|
+
# graphite doesn't support strict matching (as /\d+/), so we need to
|
84
|
+
# enumerate the hosts if a "#" wildcard is found
|
85
|
+
if ! (filter = hosts.select { |h| h =~ /#/ }).empty?
|
86
|
+
hosts_new = hosts - filter
|
87
|
+
hosts2 = Host.all.select { |h| h.multi_match(filter) }
|
88
|
+
hosts = (hosts2.map {|h| h.name } + hosts_new).sort.uniq.join(',')
|
89
|
+
end
|
90
|
+
|
91
|
+
opts[:sum] = :cluster unless opts[:zoom]
|
92
|
+
graph_url = graph.render_url(hosts.to_a, clusters, opts)
|
93
|
+
return graph_url
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_host_wildcards(graph)
|
97
|
+
return graph_opts[graph]["hosts"] || @params["hosts"] || graph["hosts"]
|
98
|
+
end
|
99
|
+
|
100
|
+
def render_global_graph(graph, opts={})
|
101
|
+
hosts = get_host_wildcards(graph)
|
102
|
+
_, clusters = get_valid_hosts(graph)
|
103
|
+
|
104
|
+
next_url = ""
|
105
|
+
type = opts[:zoom] ? :cluster : :global
|
106
|
+
options = opts.merge({:sum => type})
|
107
|
+
graph_url = graph.render_url(hosts, clusters, options)
|
108
|
+
return graph_url
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.find_by_graph(graph)
|
112
|
+
ret = []
|
113
|
+
Dashboard.each do |name, dash|
|
114
|
+
|
115
|
+
if dash["graphs"].map { |x| x.keys.first }.member?(graph.name)
|
116
|
+
ret << dash
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
return ret
|
121
|
+
end
|
122
|
+
end # Pencil::Models::Dashboard
|
123
|
+
end
|
124
|
+
end # Pencil::Models
|