gdash 0.0.1

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.
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'yaml'
4
+ require 'erb'
5
+ require 'redcarpet'
6
+
7
+ class GDash
8
+ require 'gdash/dashboard'
9
+ require 'gdash/monkey_patches'
10
+ require 'gdash/sinatra_app'
11
+ require 'graphite_graph'
12
+
13
+ attr_reader :graphite_base, :graphite_render, :dash_templates, :height, :width, :from, :until
14
+
15
+ def initialize(graphite_base, render_url, dash_templates, options={})
16
+ @graphite_base = graphite_base
17
+ @graphite_render = [@graphite_base, "/render/"].join
18
+ @dash_templates = dash_templates
19
+ @height = options.delete(:height)
20
+ @width = options.delete(:width)
21
+ @from = options.delete(:from)
22
+ @until = options.delete(:until)
23
+
24
+ raise "Dashboard templates directory #{@dash_templates} does not exist" unless File.directory?(@dash_templates)
25
+ end
26
+
27
+ def dashboard(name, options={})
28
+ options[:width] ||= @width
29
+ options[:height] ||= @height
30
+ options[:from] ||= @from
31
+ options[:until] ||= @until
32
+
33
+ Dashboard.new(name, dash_templates, options)
34
+ end
35
+
36
+ def list
37
+ dashboards.map {|dash| dash[:link]}
38
+ end
39
+
40
+ def dashboards
41
+ dashboards = []
42
+
43
+ Dir.entries(dash_templates).each do |dash|
44
+ begin
45
+ yaml_file = File.join(dash_templates, dash, "dash.yaml")
46
+ if File.exist?(yaml_file)
47
+ dashboards << YAML.load_file(yaml_file).merge({:link => dash})
48
+ end
49
+ rescue Exception => e
50
+ p e
51
+ end
52
+ end
53
+
54
+ dashboards.sort_by{|d| d[:name]}
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ class GDash
2
+ class Dashboard
3
+ attr_accessor :properties
4
+
5
+ def initialize(short_name, dir, options={})
6
+ raise "Cannot find dashboard directory #{dir}" unless File.directory?(dir)
7
+
8
+ @properties = {:graph_width => nil,
9
+ :graph_height => nil,
10
+ :graph_from => nil,
11
+ :graph_until => nil}
12
+
13
+ @properties[:short_name] = short_name
14
+ @properties[:directory] = File.join(dir, short_name)
15
+ @properties[:yaml] = File.join(dir, short_name, "dash.yaml")
16
+
17
+ raise "Cannot find YAML file #{yaml}" unless File.exist?(yaml)
18
+
19
+ @properties.merge!(YAML.load_file(yaml))
20
+
21
+ # Properties defined in dashboard config file are overridden when given on initialization
22
+ @properties[:graph_width] = options.delete(:width) || graph_width
23
+ @properties[:graph_height] = options.delete(:height) || graph_height
24
+ @properties[:graph_from] = options.delete(:from) || graph_from
25
+ @properties[:graph_until] = options.delete(:until) || graph_until
26
+ end
27
+
28
+ def graphs(options={})
29
+ options[:width] ||= graph_width
30
+ options[:height] ||= graph_height
31
+ options[:from] ||= graph_from
32
+ options[:until] ||= graph_until
33
+
34
+ graphs = Dir.entries(directory).select{|f| f.match(/\.graph$/)}
35
+
36
+ overrides = options.reject { |k,v| v.nil? }
37
+
38
+ graphs.sort.map do |graph|
39
+ {:name => File.basename(graph, ".graph"), :graphite => GraphiteGraph.new(File.join(directory, graph), overrides)}
40
+ end
41
+ end
42
+
43
+ def method_missing(method, *args)
44
+ if properties.include?(method)
45
+ properties[method]
46
+ else
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ class Array
2
+ def in_groups_of(chunk_size, padded_with=nil)
3
+ if chunk_size <= 1
4
+ if block_given?
5
+ self.each{|a| yield([a])}
6
+ else
7
+ self
8
+ end
9
+ else
10
+ arr = self.clone
11
+
12
+ # how many to add
13
+ padding = chunk_size - (arr.size % chunk_size)
14
+ padding = 0 if padding == chunk_size
15
+
16
+ # pad at the end
17
+ arr.concat([padded_with] * padding)
18
+
19
+ # how many chunks we'll make
20
+ count = arr.size / chunk_size
21
+
22
+ # make that many arrays
23
+ result = []
24
+ count.times {|s| result << arr[s * chunk_size, chunk_size]}
25
+
26
+ if block_given?
27
+ result.each{|a| yield(a)}
28
+ else
29
+ result
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ class GraphiteGraph
36
+ attr_accessor :properties, :file
37
+ end
@@ -0,0 +1,162 @@
1
+ class GDash
2
+ class SinatraApp < ::Sinatra::Base
3
+ def initialize(graphite_base, graph_templates, options = {})
4
+ # where the whisper data is
5
+ @whisper_dir = options.delete(:whisper_dir) || "/var/lib/carbon/whisper"
6
+
7
+ # where graphite lives
8
+ @graphite_base = graphite_base
9
+
10
+ # where the graphite renderer is
11
+ @graphite_render = [@graphite_base, "/render/"].join
12
+
13
+ # where to find graph, dash etc templates
14
+ @graph_templates = graph_templates
15
+
16
+ # the dash site might have a prefix for its css etc
17
+ @prefix = options.delete(:prefix) || ""
18
+
19
+ # the page refresh rate
20
+ @refresh_rate = options.delete(:refresh_rate) || 60
21
+
22
+ # how many columns of graphs do you want on a page
23
+ @graph_columns = options.delete(:graph_columns) || 2
24
+
25
+ # how wide each graph should be
26
+ @graph_width = options.delete(:graph_width)
27
+
28
+ # how hight each graph sould be
29
+ @graph_height = options.delete(:graph_height)
30
+
31
+ # Dashboard title
32
+ @dash_title = options.delete(:title) || "Graphite Dashboard"
33
+
34
+ # Time filters in interface
35
+ @interval_filters = options.delete(:interval_filters) || Array.new
36
+
37
+ @intervals = options.delete(:intervals) || []
38
+
39
+ @top_level = Hash.new
40
+ Dir.entries(@graph_templates).each do |category|
41
+ if File.directory?("#{@graph_templates}/#{category}")
42
+ unless ("#{category}" =~ /^\./ )
43
+ @top_level["#{category}"] = GDash.new(@graphite_base, "/render/", File.join(@graph_templates, "/#{category}"), {:width => @graph_width, :height => @graph_height})
44
+ end
45
+ end
46
+ end
47
+
48
+ super()
49
+ end
50
+
51
+ set :static, true
52
+ set :views, File.join(File.expand_path(File.dirname(__FILE__)), "../..", "views")
53
+ if Sinatra.const_defined?("VERSION") && Gem::Version.new(Sinatra::VERSION) >= Gem::Version.new("1.3.0")
54
+ set :public_folder, File.join(File.expand_path(File.dirname(__FILE__)), "../..", "public")
55
+ else
56
+ set :public, File.join(File.expand_path(File.dirname(__FILE__)), "../..", "public")
57
+ end
58
+
59
+ get '/' do
60
+ if @top_level.empty?
61
+ @error = "No dashboards found in the templates directory"
62
+ end
63
+
64
+ erb :index
65
+ end
66
+
67
+ get '/:category/:dash/details/:name' do
68
+ if @top_level["#{params[:category]}"].list.include?(params[:dash])
69
+ @dashboard = @top_level[@params[:category]].dashboard(params[:dash])
70
+ else
71
+ @error = "No dashboard called #{params[:dash]} found in #{params[:category]}/#{@top_level[params[:category]].list.join ','}."
72
+ end
73
+
74
+ if @intervals.empty?
75
+ @error = "No intervals defined in configuration"
76
+ end
77
+
78
+ if main_graph = @dashboard.graphs[params[:name].to_i][:graphite]
79
+ @graphs = @intervals.map do |e|
80
+ new_props = {:from => e[0], :title => "#{main_graph.properties[:title]} - #{e[1]}"}
81
+ new_props = main_graph.properties.merge new_props
82
+ GraphiteGraph.new(main_graph.file, new_props)
83
+ end
84
+ else
85
+ @error = "No such graph available"
86
+ end
87
+
88
+ erb :detailed_dashboard
89
+ end
90
+
91
+ get '/:category/:dash/full/?*' do
92
+ options = {}
93
+ params["splat"] = params["splat"].first.split("/")
94
+
95
+ params["columns"] = params["splat"][0].to_i || @graph_columns
96
+
97
+ if params["splat"].size == 3
98
+ options[:width] = params["splat"][1].to_i
99
+ options[:height] = params["splat"][2].to_i
100
+ else
101
+ options[:width] = @graph_width
102
+ options[:height] = @graph_height
103
+ end
104
+
105
+ options.merge!(query_params)
106
+
107
+ if @top_level["#{params[:category]}"].list.include?(params[:dash])
108
+ @dashboard = @top_level[@params[:category]].dashboard(params[:dash], options)
109
+ else
110
+ @error = "No dashboard called #{params[:dash]} found in #{params[:category]}/#{@top_level[params[:category]].list.join ','}"
111
+ end
112
+
113
+ erb :full_size_dashboard, :layout => false
114
+ end
115
+
116
+ get '/:category/:dash/?*' do
117
+ options = {}
118
+ params["splat"] = params["splat"].first.split("/")
119
+
120
+ case params["splat"][0]
121
+ when 'time'
122
+ options[:from] = params["splat"][1] || "-1hour"
123
+ options[:until] = params["splat"][2] || "now"
124
+ end
125
+
126
+ options.merge!(query_params)
127
+
128
+ if @top_level["#{params[:category]}"].list.include?(params[:dash])
129
+ @dashboard = @top_level[@params[:category]].dashboard(params[:dash], options)
130
+ else
131
+ @error = "No dashboard called #{params[:dash]} found in #{params[:category]}/#{@top_level[params[:category]].list.join ','}."
132
+ end
133
+
134
+ erb :dashboard
135
+ end
136
+
137
+ get '/docs/' do
138
+ markdown :README, :layout_engine => :erb
139
+ end
140
+
141
+ helpers do
142
+ include Rack::Utils
143
+
144
+ alias_method :h, :escape_html
145
+
146
+ def link_to_interval(options)
147
+ "<a href=\"#{ [@prefix, params[:category], params[:dash], 'time', h(options[:from]), h(options[:to])].join('/') }\">#{ h(options[:label]) }</a>"
148
+ end
149
+
150
+ def query_params
151
+ hash = {}
152
+ protected_keys = [:category, :dash, :splat]
153
+
154
+ params.each do |k, v|
155
+ hash[k.to_sym] = v unless protected_keys.include?(k.to_sym)
156
+ end
157
+
158
+ hash
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,104 @@
1
+ /* ==========================================================
2
+ * bootstrap-alerts.js v1.3.0
3
+ * http://twitter.github.com/bootstrap/javascript.html#alerts
4
+ * ==========================================================
5
+ * Copyright 2011 Twitter, Inc.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ * ========================================================== */
19
+
20
+
21
+ !function( $ ){
22
+
23
+ /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
24
+ * ======================================================= */
25
+
26
+ var transitionEnd
27
+
28
+ $(document).ready(function () {
29
+
30
+ $.support.transition = (function () {
31
+ var thisBody = document.body || document.documentElement
32
+ , thisStyle = thisBody.style
33
+ , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
34
+ return support
35
+ })()
36
+
37
+ // set CSS transition event type
38
+ if ( $.support.transition ) {
39
+ transitionEnd = "TransitionEnd"
40
+ if ( $.browser.webkit ) {
41
+ transitionEnd = "webkitTransitionEnd"
42
+ } else if ( $.browser.mozilla ) {
43
+ transitionEnd = "transitionend"
44
+ } else if ( $.browser.opera ) {
45
+ transitionEnd = "oTransitionEnd"
46
+ }
47
+ }
48
+
49
+ })
50
+
51
+ /* ALERT CLASS DEFINITION
52
+ * ====================== */
53
+
54
+ var Alert = function ( content, selector ) {
55
+ this.$element = $(content)
56
+ .delegate(selector || '.close', 'click', this.close)
57
+ }
58
+
59
+ Alert.prototype = {
60
+
61
+ close: function (e) {
62
+ var $element = $(this).parent('.alert-message')
63
+
64
+ e && e.preventDefault()
65
+ $element.removeClass('in')
66
+
67
+ function removeElement () {
68
+ $element.remove()
69
+ }
70
+
71
+ $.support.transition && $element.hasClass('fade') ?
72
+ $element.bind(transitionEnd, removeElement) :
73
+ removeElement()
74
+ }
75
+
76
+ }
77
+
78
+
79
+ /* ALERT PLUGIN DEFINITION
80
+ * ======================= */
81
+
82
+ $.fn.alert = function ( options ) {
83
+
84
+ if ( options === true ) {
85
+ return this.data('alert')
86
+ }
87
+
88
+ return this.each(function () {
89
+ var $this = $(this)
90
+
91
+ if ( typeof options == 'string' ) {
92
+ return $this.data('alert')[options]()
93
+ }
94
+
95
+ $(this).data('alert', new Alert( this ))
96
+
97
+ })
98
+ }
99
+
100
+ $(document).ready(function () {
101
+ new Alert($('body'), '.alert-message[data-alert] .close')
102
+ })
103
+
104
+ }( window.jQuery || window.ender );
@@ -0,0 +1,50 @@
1
+ /* ============================================================
2
+ * bootstrap-dropdown.js v1.3.0
3
+ * http://twitter.github.com/bootstrap/javascript.html#dropdown
4
+ * ============================================================
5
+ * Copyright 2011 Twitter, Inc.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ * ============================================================ */
19
+
20
+
21
+ !function( $ ){
22
+
23
+ var d = 'a.menu, .dropdown-toggle'
24
+
25
+ function clearMenus() {
26
+ $(d).parent('li').removeClass('open')
27
+ }
28
+
29
+ $(function () {
30
+ $('html').bind("click", clearMenus)
31
+ $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' )
32
+ })
33
+
34
+ /* DROPDOWN PLUGIN DEFINITION
35
+ * ========================== */
36
+
37
+ $.fn.dropdown = function ( selector ) {
38
+ return this.each(function () {
39
+ $(this).delegate(selector || d, 'click', function (e) {
40
+ var li = $(this).parent('li')
41
+ , isActive = li.hasClass('open')
42
+
43
+ clearMenus()
44
+ !isActive && li.toggleClass('open')
45
+ return false
46
+ })
47
+ })
48
+ }
49
+
50
+ }( window.jQuery || window.ender );