Skip to content
Browse files

Added support for interval filters and url based filters.

Dimension and filters are now configurable using dashbord confi file and can be overridden by url param.
  • Loading branch information...
1 parent 39a3ae4 commit 2db50a38ce1e9645ee2b1ed54cbc683070b25ab5 @tanelj tanelj committed with Feb 15, 2012
Showing with 125 additions and 26 deletions.
  1. +35 −0 README.md
  2. +12 −0 config/gdash.yaml-sample
  3. +12 −8 lib/gdash.rb
  4. +19 −8 lib/gdash/dashboard.rb
  5. +41 −10 lib/gdash/sinatra_app.rb
  6. +5 −0 views/_interval_filter.erb
  7. +1 −0 views/dashboard.erb
View
35 README.md
@@ -32,6 +32,7 @@ and additional options:
* The width of the graphs, 500 by default
* The height of the graphs, 250 by default
* Where your whisper files are stored - future use
+ * Optional interval quick filters
Creating Dashboards?
--------------------
@@ -104,6 +105,39 @@ each with numerous graphs.
You can create as many groups as you want each with many dashboards inside.
+Custom Time Intervals?
+--------------------
+
+You can reuse your dashboards and adjust the time interval by using the following url
+structure:
+
+ http://gdash.example.com/dashboard/email/time/-8d/-7d
+
+or
+
+ http://gdash.example.com/dashboard/email/?from=-8d&to=-7d
+ http://gdash.example.com/dashboard/email/full/2/600/300?from=-8d&to=-7d
+
+This will display the _email_ dashboard with a time interval same day last week.
+If you hit */dashboard/email/time/* it will default to the past hour (*-1hour*)
+See http://graphite.readthedocs.org/en/1.0/url-api.html#from-until for more info
+acceptable *from* and *until* values.
+
+Quick interval filters shown in interface are configurable in _gdash.yaml_ options sections. Eg:
+
+ :options:
+ :interval_filters:
+ - :label: Last Hour
+ :from: -1h
+ :to: now
+ - :label: Last Day
+ :from: -1day
+ - :label: Current Week
+ :from: monday
+ :to: now
+
+Quick filter is not shown when *interval_filters* section is missing in configuration file.
+
Time Intervals Display?
-----------------------
@@ -129,6 +163,7 @@ You can reuse your dashboards for big displays against a wall in your NOC or off
by using the following url structure:
http://gdash.example.com/dashboard/email/full/4/600/300
+ http://gdash.example.com/dashboard/email/full/4?width=600&height=300
This will display the _email_ dashboard in _4_ columns each graph with a width of
_600_ and a height of _300_
View
12 config/gdash.yaml-sample
@@ -10,6 +10,18 @@
:graph_width: 500
:graph_height: 250
:whisper_dir: "/var/lib/carbon/whisper"
+ :interval_filters:
+ - :label: Last Hour
+ :from: -1hour
+ :to: now
+ - :label: Last Day
+ :from: -1day
+ - :label: Last Week
+ :from: -1week
+ - :label: Last Month
+ :from: -1month
+ - :label: Last Year
+ :from: -1year
:intervals:
- [ "-1hour", "1 hour" ]
- [ "-2hour", "2 hour" ]
View
20 lib/gdash.rb
@@ -9,23 +9,27 @@ class GDash
require 'gdash/sinatra_app'
require 'graphite_graph'
- attr_reader :graphite_base, :graphite_render, :dash_templates, :height, :width
+ attr_reader :graphite_base, :graphite_render, :dash_templates, :height, :width, :from, :until
- def initialize(graphite_base, render_url, dash_templates, width=500, height=250)
+ def initialize(graphite_base, render_url, dash_templates, options={})
@graphite_base = graphite_base
@graphite_render = [@graphite_base, "/render/"].join
@dash_templates = dash_templates
- @height = height
- @width = width
+ @height = options.delete(:height)
+ @width = options.delete(:width)
+ @from = options.delete(:from)
+ @until = options.delete(:until)
raise "Dashboard templates directory #{@dash_templates} does not exist" unless File.directory?(@dash_templates)
end
- def dashboard(name, width=nil, height=nil)
- width ||= @width
- height ||= @height
+ def dashboard(name, options={})
+ options[:width] ||= @width
+ options[:height] ||= @height
+ options[:from] ||= @from
+ options[:until] ||= @until
- Dashboard.new(name, dash_templates, width, height)
+ Dashboard.new(name, dash_templates, options)
end
def list
View
27 lib/gdash/dashboard.rb
@@ -2,30 +2,41 @@ class GDash
class Dashboard
attr_accessor :properties
- def initialize(short_name, dir, graph_width=500, graph_height=250)
+ def initialize(short_name, dir, options={})
raise "Cannot find dashboard directory #{dir}" unless File.directory?(dir)
- @properties = {}
+ @properties = {:graph_width => nil,
+ :graph_height => nil,
+ :graph_from => nil,
+ :graph_until => nil}
@properties[:short_name] = short_name
@properties[:directory] = File.join(dir, short_name)
@properties[:yaml] = File.join(dir, short_name, "dash.yaml")
- @properties[:graph_width] = graph_width
- @properties[:graph_height] = graph_height
raise "Cannot find YAML file #{yaml}" unless File.exist?(yaml)
@properties.merge!(YAML.load_file(yaml))
+
+ # Properties defined in dashboard config file are overridden when given on initialization
+ @properties[:graph_width] = options.delete(:width) || graph_width
+ @properties[:graph_height] = options.delete(:height) || graph_height
+ @properties[:graph_from] = options.delete(:from) || graph_from
+ @properties[:graph_until] = options.delete(:until) || graph_until
end
- def graphs(width=nil, height=nil)
- height ||= graph_height
- width ||= graph_width
+ def graphs(options={})
+ options[:width] ||= graph_width
+ options[:height] ||= graph_height
+ options[:from] ||= graph_from
+ options[:until] ||= graph_until
graphs = Dir.entries(directory).select{|f| f.match(/\.graph$/)}
+ overrides = options.reject { |k,v| v.nil? }
+
graphs.sort.map do |graph|
- {:name => File.basename(graph, ".graph"), :graphite => GraphiteGraph.new(File.join(directory, graph), {:height => height, :width => width})}
+ {:name => File.basename(graph, ".graph"), :graphite => GraphiteGraph.new(File.join(directory, graph), overrides)}
end
end
View
51 lib/gdash/sinatra_app.rb
@@ -23,21 +23,24 @@ def initialize(graphite_base, graph_templates, options = {})
@graph_columns = options.delete(:graph_columns) || 2
# how wide each graph should be
- @graph_width = options.delete(:graph_width) || 500
+ @graph_width = options.delete(:graph_width)
# how hight each graph sould be
- @graph_height = options.delete(:graph_height) || 250
+ @graph_height = options.delete(:graph_height)
# Dashboard title
@dash_title = options.delete(:title) || "Graphite Dashboard"
+ # Time filters in interface
+ @interval_filters = options.delete(:interval_filters) || Array.new
+
@intervals = options.delete(:intervals) || []
@top_level = Hash.new
Dir.entries(@graph_templates).each do |category|
if File.directory?("#{@graph_templates}/#{category}")
unless ("#{category}" =~ /^\./ )
- @top_level["#{category}"] = GDash.new(@graphite_base, "/render/", File.join(@graph_templates, "/#{category}"), @graph_width, @graph_height)
+ @top_level["#{category}"] = GDash.new(@graphite_base, "/render/", File.join(@graph_templates, "/#{category}"), {:width => @graph_width, :height => @graph_height})
end
end
end
@@ -86,31 +89,44 @@ def initialize(graphite_base, graph_templates, options = {})
end
get '/:category/:dash/full/?*' do
+ options = {}
params["splat"] = params["splat"].first.split("/")
params["columns"] = params["splat"][0].to_i || @graph_columns
if params["splat"].size == 3
- width = params["splat"][1].to_i
- height = params["splat"][2].to_i
+ options[:width] = params["splat"][1].to_i
+ options[:height] = params["splat"][2].to_i
else
- width = @graph_width
- height = @graph_height
+ options[:width] = @graph_width
+ options[:height] = @graph_height
end
+ options.merge!(query_params)
if @top_level["#{params[:category]}"].list.include?(params[:dash])
- @dashboard = @top_level[@params[:category]].dashboard(params[:dash], width, height)
+ @dashboard = @top_level[@params[:category]].dashboard(params[:dash], options)
else
@error = "No dashboard called #{params[:dash]} found in #{params[:category]}/#{@top_level[params[:category]].list.join ','}"
end
erb :full_size_dashboard, :layout => false
end
- get '/:category/:dash/' do
+ get '/:category/:dash/?*' do
+ options = {}
+ params["splat"] = params["splat"].first.split("/")
+
+ case params["splat"][0]
+ when 'time'
+ options[:from] = params["splat"][1] || "-1hour"
+ options[:until] = params["splat"][2] || "now"
+ end
+
+ options.merge!(query_params)
+
if @top_level["#{params[:category]}"].list.include?(params[:dash])
- @dashboard = @top_level[@params[:category]].dashboard(params[:dash])
+ @dashboard = @top_level[@params[:category]].dashboard(params[:dash], options)
else
@error = "No dashboard called #{params[:dash]} found in #{params[:category]}/#{@top_level[params[:category]].list.join ','}."
end
@@ -122,6 +138,21 @@ def initialize(graphite_base, graph_templates, options = {})
include Rack::Utils
alias_method :h, :escape_html
+
+ def link_to_interval(options)
+ "<a href=\"#{ [@prefix, params[:category], params[:dash], 'time', h(options[:from]), h(options[:to])].join('/') }\">#{ h(options[:label]) }</a>"
+ end
+
+ def query_params
+ hash = {}
+ protected_keys = [:category, :dash, :splat]
+
+ params.each do |k, v|
+ hash[k.to_sym] = v unless protected_keys.include?(k.to_sym)
+ end
+
+ hash
+ end
end
end
end
View
5 views/_interval_filter.erb
@@ -0,0 +1,5 @@
+<ul class="pills" style="margin-bottom: 0px;">
+ <% @interval_filters.each do |options| %>
+ <li<%= ' class="active"' if options[:from] == @dashboard.graph_from %>><%= link_to_interval(options) %></li>
+ <% end %>
+</ul>
View
1 views/dashboard.erb
@@ -1,5 +1,6 @@
<% unless @error %>
<h1><%= @dashboard.name %>&nbsp;<small><%= @dashboard.description %></small></h1>
+<%= erb :_interval_filter, :layout => false unless @interval_filters.empty? %>
<div class="row">
<table>
<% row = 1 %>

0 comments on commit 2db50a3

Please sign in to comment.
Something went wrong with that request. Please try again.