On Tue, Jul 20, 2010 at 12:49:04PM -0400, [email protected] wrote: > From: martyntaylor <[email protected]> > > --- > src/app/controllers/dashboard_controller.rb | 18 +- > src/app/models/graph.rb | 28 +++ > src/app/services/graph_service.rb | 320 > +++++++++++++++++---------- > src/app/views/dashboard/summary.haml | 8 +- > 4 files changed, 255 insertions(+), 119 deletions(-) > > diff --git a/src/app/controllers/dashboard_controller.rb > b/src/app/controllers/dashboard_controller.rb > index bcd2073..757598d 100644 > --- a/src/app/controllers/dashboard_controller.rb > +++ b/src/app/controllers/dashboard_controller.rb > @@ -31,17 +31,25 @@ class DashboardController < ApplicationController > return params[:ajax] == "true" > end > > - def provider_qos_graph > + def provider_qos_avg_time_to_submit_graph > params[:provider] = Provider.find(params[:id]) > - graph = GraphService.dashboard_qos(current_user, > params)[params[:provider]][Graph::QOS_AVG_TIME_TO_SUBMIT] > + graph = > GraphService.dashboard_qos_avg_time_to_submit_graph(current_user, > params)[params[:provider]][Graph::QOS_AVG_TIME_TO_SUBMIT] > respond_to do |format| > format.svg { render :xml => graph.svg} > end > end > > - def account_quota_graph > - params[:account] = CloudAccount.find(params[:id]) > - graph = GraphService.dashboard_quota(current_user, > params)[params[:account]][Graph::QUOTA_INSTANCES_IN_USE] > + def quota_usage_graph > + if params[:cloud_account_id] > + params[:parent] = CloudAccount.find(params[:cloud_account_id]) > + elsif params[:pool_id] > + params[:parent] = Pool.find(params[:pool_id]) > + else > + return nil > + end > + > + graphs = GraphService.dashboard_quota_usage(current_user, params) > + graph = > graphs[params[:parent]][Graph.get_quota_usage_graph_name(params[:resource_name])] > respond_to do |format| > format.svg { render :xml => graph.svg} > end > diff --git a/src/app/models/graph.rb b/src/app/models/graph.rb > index 1c20dc3..cf1b498 100644 > --- a/src/app/models/graph.rb > +++ b/src/app/models/graph.rb > @@ -4,7 +4,35 @@ class Graph > QOS_AVG_TIME_TO_SUBMIT = "qos_avg_time_to_submit" > QUOTA_INSTANCES_IN_USE = "quota_instances_in_use" > INSTANCES_BY_PROVIDER_PIE = "instances_by_provider_pie" > + > + # Quota Usage Graphs > + QUOTA_USAGE_RUNNING_INSTANCES = "quota_utilization_running_instances" > + QUOTA_USAGE_RUNNING_MEMORY = "quota_utilization_running_memory" > + QUOTA_USAGE_RUNNING_CPUS = "quota_utilization_running_cpus" > + QUOTA_USAGE_TOTAL_INSTANCES = "quota_utilization_total_instances" > + QUOTA_USAGE_TOTAL_STORAGE = "quota_utilization_total_storage" > + QUOTA_USAGE_OVERALL = "quota_utilization_overall" > + > def initialize > @svg = "" > end > + > + def self.get_quota_usage_graph_name(resource_name) > + case resource_name > + when Quota::RESOURCE_RUNNING_INSTANCES > + return QUOTA_USAGE_RUNNING_INSTANCES > + when Quota::RESOURCE_RUNNING_MEMORY > + return QUOTA_USAGE_RUNNING_MEMORY > + when Quota::RESOURCE_RUNNING_CPUS > + return QUOTA_USAGE_RUNNING_CPUS > + when Quota::RESOURCE_TOTAL_INSTANCES > + return QUOTA_USAGE_TOTAL_INSTANCES > + when Quota::RESOURCE_TOTAL_STORAGE > + return QUOTA_USAGE_TOTAL_STORAGE > + when Quota::RESOURCE_OVERALL > + return QUOTA_USAGE_OVERALL > + else > + return nil > + end > + end > end > diff --git a/src/app/services/graph_service.rb > b/src/app/services/graph_service.rb > index 11da01c..6c17743 100644 > --- a/src/app/services/graph_service.rb > +++ b/src/app/services/graph_service.rb > @@ -3,6 +3,8 @@ class GraphService > require 'nokogiri' > require 'scruffy' > > + DATA_SERVICE = DataServiceActiveRecord > + > def self.dashboard_quota (user,opts = {}) > #FIXME add permission checks to filter what graphs user can get > graphs = Hash.new > @@ -12,7 +14,7 @@ class GraphService > if opts[:cloud_account] > cloud_account = opts[:cloud_account] > cloud_account_graphs = Hash.new > - cloud_account_graphs[Graph::QUOTA_INSTANCES_IN_USE] = > quota_instances_in_use_graph(cloud_account,opts) > + cloud_account_graphs[Graph::QUOTA_INSTANCES_IN_USE] = > qos_failure_rate_graph(parent, opts = {}) > graphs[cloud_account] = cloud_account_graphs > else > CloudAccount.all.each do |cloud_account| > @@ -24,7 +26,16 @@ class GraphService > graphs > end > > - def self.dashboard_qos (user,opts = {}) > + def self.dashboard_quota_usage(user, opts = {}) > + parent = opts[:parent] > + > + graphs = Hash.new > + graphs[parent] = quota_usage_graph(parent, opts) > + > + return graphs > + end > + > + def self.dashboard_qos_avg_time_to_submit_graph(user, opts = {}) > #FIXME add permission checks to filter what graphs user can get > graphs = Hash.new > > @@ -58,7 +69,198 @@ class GraphService > output_stream = IO::popen( cmd, "r+") > end > > - def self.quota_instances_in_use_graph (cloud_account, opts = {}) > + def self.quota_usage_graph (parent, opts = {}) > + x = [1,2] > + > + #we'll just have zero values for the unexpected case where cloud_account > has no quota > + y = x.collect { |v| 0 } > + if parent.quota > + quota = parent.quota > + data_point = DataServiceActiveRecord.quota_usage(parent, > opts[:resource_name]) > + #Handle No Limit case > + if data_point.max == Quota::NO_LIMIT > + y = [data_point.used, nil] > + else > + y = [data_point.used, data_point.max] > + end > + end > + > + chart_opts = {:x => x, :y => y} > + > + graphs = Hash.new > + graphs[Graph.get_quota_usage_graph_name(opts[:resource_name])] = > draw_bar_chart(opts, chart_opts) > + return graphs > + end > + > + def self.qos_avg_time_to_submit_graph(parent, opts = {}) > + start_time = Time.parse(opts[:start_time]) > + end_time = Time.parse(opts[:end_time]) > + interval_length = opts[:interval_length].to_f > + action = opts[:task_action] > + > + stats = DATA_SERVICE.qos_task_submission_stats(parent, start_time, > end_time, interval_length, action) > + data = get_data_from_stats(stats, "average") > + draw_line_graph(opts, data) > + end > + > + def self.qos_failure_rate_graph(parent, opts = {}) > + start_time = Time.parse(opts[:start_time]) > + end_time = Time.parse(opts[:end_time]) > + interval_length = opts[:interval_length].to_f > + failure_code = opts[:failure_code] > + > + stats = DATA_SERVICE.qos_failure_rate_stats(parent, start_time, > end_time, interval_length, failure_code) > + data = get_data_from_stats(stats, "failure_rate") > + data[:y_range] = "[0:100]" > + draw_line_graph(opts, data) > + end > + > + def self.qos_avg_time_to_complete_life_cycle_event(parent, opts = {}) > + start_time = Time.parse(opts[:start_time]) > + end_time = Time.parse(opts[:end_time]) > + interval_length = opts[:interval_length].to_f > + action = opts[:task_action] > + > + stats = DATA_SERVICE.qos_task_completion_stats(parent, start_time, > end_time, interval_length, action) > + data = get_data_from_stats(stats, "average") > + draw_line_graph(opts, data) > + end > + > + def self.instances_by_provider_pie (opts = {}) > + pie_opts = {} > + providers = Provider.all > + providers.each do |provider| > + running_instances = 0 > + provider.cloud_accounts.each do |account| > + running_instances = running_instances + > account.quota.running_instances if account.quota > + end > + if running_instances > 0 > + pie_opts[:"#{provider.name}"] = running_instances > + end > + end > + > + return draw_pie_chart(opts, pie_opts) > + end > + > + def self.get_data_from_stats(stats, type) > + x = [] > + y = [] > + y_max = 0 > + for i in 0...stats.length do > + x << i > + y_value = stats[i][type] > + if y_value > + y << y_value > + if y_value > y_max > + y_max = y_value > + end > + else > + y << 0 > + end > + end > + > + if y_max == 0 > + y_max = 1 > + else > + y_max = y_max * 1.1 > + end > + > + y_range = "[0:" + y_max.to_s + "]" > + return { :x => x, :y => y, :y_range => y_range } > + end > + > + def self.draw_pie_chart(opts, pie_opts) > + #things we're checking for in opts: :height, :width > + height = 200 unless opts[:height].nil? ? nil : height = > opts[:height].to_i > + width = 300 unless opts[:width].nil? ? nil : width = opts[:width].to_i > + > + graph = Graph.new > + > + mytheme = Scruffy::Themes::Keynote.new > + mytheme.background = :white > + mytheme.marker = :black #sets the label text color > + mytheme.colors = %w(#00689a #00b0e0) > + > + scruffy_graph = Scruffy::Graph.new({:theme => mytheme}) > + scruffy_graph.renderer = Scruffy::Renderers::Pie.new > + scruffy_graph.add :pie, '', pie_opts > + > + raw_svg = scruffy_graph.render :width => width, :height => height > + > + xml = Nokogiri::XML(raw_svg) > + svg = xml.css 'svg' > + svg.each do |node| > + node.set_attribute 'viewBox',"0 0 #{width} #{height}" > + end > + > + xml.root.traverse do |node| > + if node.name == 'text' > + if node.has_attribute? 'font-family' > + node.set_attribute 'font-family','sans-serif' > + end > + if (node.has_attribute? 'font-size') && > node.get_attribute('font-size').length > 0 > + size = node.get_attribute('font-size').to_f > + size = size * 1.5 > + node.set_attribute 'font-size',size.to_s > + end > + end > + end > + > + graph.svg = xml.to_s > + graph > + end > + > + def self.draw_line_graph(opts, data) > + #things we're checking for in opts: :height, :width > + > + height = 60 unless opts[:height].nil? ? nil : height = opts[:height].to_i > + width = 100 unless opts[:width].nil? ? nil : width = opts[:width].to_i > + > + graph = Graph.new > + gp = gnuplot_open > + > + Gnuplot::Plot.new( gp ) do |plot| > + plot.terminal "svg size #{width},#{height}" > + plot.arbitrary_lines << "unset xtics" > + plot.arbitrary_lines << "unset x2tics" > + plot.arbitrary_lines << "unset ytics" > + plot.arbitrary_lines << "unset y2tics" > + plot.set "bmargin","0" > + plot.set "lmargin","1" > + plot.set "rmargin","0" > + plot.set "tmargin","0" > + > + #FIXME: get data from DataService for the provider. > + #For demo, plot a random walk for demo of graph display until we hook > into DataService > + #First build two equal-length arrays > + #x = (0..500).collect { |v| v.to_f } > + > + #walk = 0 > + #y = x.collect { |v| rand > 0.5 ? walk = walk + 1 : walk = walk - 1 } > + > + x = data[:x] > + y = data[:y] > + y_range = data[:y_range] > + > + #plot.set "yrange [-50:50]" > + plot.set "yrange " + y_range > + > + #This type of plot takes two equal length arrays of numbers as input. > + plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds| > + ds.using = "1:2" > + ds.with = "lines" > + ds.notitle > + end > + end > + gp.flush > + gp.close_write > + gp.read(nil,graph.svg) > + gp.close_read > + graph > + end > + > + def self.draw_bar_chart(opts, chart_opts) > + > #things we're checking for in opts: :max_value, :height, :width > > unless max_value = opts[:max_value] > @@ -67,7 +269,6 @@ class GraphService > height = 80 unless opts[:height].nil? ? nil : height = opts[:height].to_i > width = 150 unless opts[:width].nil? ? nil : width = opts[:width].to_i > > - > raw_svg = "" > gp = gnuplot_open > Gnuplot::Plot.new( gp ) do |plot| > @@ -87,14 +288,8 @@ class GraphService > plot.set "xrange [.25:2.75]" > plot.set "yrange [0:#{max_value * 1.5}]" #we want to scale maxvalue > 50% larger to leave room for label > > - x = [1,2] > - #we'll just have zero values for the unexpected case where > cloud_account has no quota > - y = x.collect { |v| 0 } > - if cloud_account.quota > - quota = cloud_account.quota > - y = [quota.running_instances,quota.maximum_running_instances] > - end > - > + x = chart_opts[:x] > + y = chart_opts[:y] > > #The two arrays above are three columns of data for gnuplot. > plot.data << Gnuplot::DataSet.new( [[x[0]], [y[0]]] ) do |ds| > @@ -155,105 +350,6 @@ class GraphService > graph = Graph.new > graph.svg = modified_svg > graph > - > - end > - > - def self.qos_avg_time_to_submit_graph (provider, opts = {}) > - #things we're checking for in opts: :height, :width > - > - height = 60 unless opts[:height].nil? ? nil : height = opts[:height].to_i > - width = 100 unless opts[:width].nil? ? nil : width = opts[:width].to_i > - > - graph = Graph.new > - gp = gnuplot_open > - > - Gnuplot::Plot.new( gp ) do |plot| > - plot.terminal "svg size #{width},#{height}" > - plot.arbitrary_lines << "unset xtics" > - plot.arbitrary_lines << "unset x2tics" > - plot.arbitrary_lines << "unset ytics" > - plot.arbitrary_lines << "unset y2tics" > - plot.set "bmargin","0" > - plot.set "lmargin","1" > - plot.set "rmargin","0" > - plot.set "tmargin","0" > - > - #FIXME: get data from DataService for the provider. > - #For demo, plot a random walk for demo of graph display until we hook > into DataService > - #First build two equal-length arrays > - x = (0..500).collect { |v| v.to_f } > - > - walk = 0 > - y = x.collect { |v| rand > 0.5 ? walk = walk + 1 : walk = walk - 1 } > - plot.set "yrange [-50:50]" > - > - #This type of plot takes two equal length arrays of numbers as input. > - plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds| > - ds.using = "1:2" > - ds.with = "lines" > - ds.notitle > - end > - end > - gp.flush > - gp.close_write > - gp.read(nil,graph.svg) > - gp.close_read > - graph > - end > - > - def self.instances_by_provider_pie (opts = {}) > - #things we're checking for in opts: :height, :width > - > - height = 200 unless opts[:height].nil? ? nil : height = > opts[:height].to_i > - width = 300 unless opts[:width].nil? ? nil : width = opts[:width].to_i > - > - graph = Graph.new > - > - mytheme = Scruffy::Themes::Keynote.new > - mytheme.background = :white > - mytheme.marker = :black #sets the label text color > - mytheme.colors = %w(#00689a #00b0e0) > - > - scruffy_graph = Scruffy::Graph.new({:theme => mytheme}) > - scruffy_graph.renderer = Scruffy::Renderers::Pie.new > - > - pie_opts = {} > - providers = Provider.all > - providers.each do |provider| > - running_instances = 0 > - provider.cloud_accounts.each do |account| > - running_instances = running_instances + > account.quota.running_instances if account.quota > - end > - if running_instances > 0 > - pie_opts[:"#{provider.name}"] = running_instances > - end > - end > - > - scruffy_graph.add :pie, '', pie_opts > - > - raw_svg = scruffy_graph.render :width => width, :height => height > - > - xml = Nokogiri::XML(raw_svg) > - svg = xml.css 'svg' > - svg.each do |node| > - node.set_attribute 'viewBox',"0 0 #{width} #{height}" > - end > - > - xml.root.traverse do |node| > - if node.name == 'text' > - if node.has_attribute? 'font-family' > - node.set_attribute 'font-family','sans-serif' > - end > - if (node.has_attribute? 'font-size') && > node.get_attribute('font-size').length > 0 > - size = node.get_attribute('font-size').to_f > - size = size * 1.5 > - node.set_attribute 'font-size',size.to_s > - end > - end > - end > - > - graph.svg = xml.to_s > - graph > end > > -end > +end > \ No newline at end of file > diff --git a/src/app/views/dashboard/summary.haml > b/src/app/views/dashboard/summary.haml > index 66aedde..f3a21d3 100644 > --- a/src/app/views/dashboard/summary.haml > +++ b/src/app/views/dashboard/summary.haml > @@ -60,7 +60,11 @@ > %div{ :style => "clear:both"} > - @providers.each do |provider| > .provider_service_quality_graph > - = "<object data='" + url_for(:action => :provider_qos_graph, :id > => provider.id, :width => 100, :height => 50) + "' type='image/svg+xml' />" > + -end_time = Time.now > + -start_time = end_time - (24 * 60 * 60) > + -interval_length = 3600 > + -task_action = "create" > + = "<object data='" + url_for(:action => > :provider_qos_avg_time_to_submit_graph, :id => provider.id, :start_time => > start_time, :end_time => end_time, :interval_length => interval_length, > :task_action => task_action, :width => 100, :height => 50) + "' > type='image/svg+xml' />" > .provider_service_quality_graph_summary > = provider.name > <!-- FIXME 'good/poor/average service... --> > @@ -109,7 +113,7 @@ > .account_quota_usage_graph_summary > = account.provider.name + ": " + account.name > .account_quota_usage_current_graph > - %object{ :data => url_for(:action => :account_quota_graph, :id => > account.id, :width => 100, :height => 50), :type => 'image/svg+xml'} > + %object{ :data => url_for(:action => :quota_usage_graph, > :cloud_account_id => account.id, :resource_name => > Quota::RESOURCE_RUNNING_INSTANCES, :width => 100, :height => 50), :type => > 'image/svg+xml'} > <div style="clear: both;" /> > > :javascript > -- > 1.7.1.1 > > _______________________________________________ > deltacloud-devel mailing list > [email protected] > https://fedorahosted.org/mailman/listinfo/deltacloud-devel
ACK, sir! _______________________________________________ deltacloud-devel mailing list [email protected] https://fedorahosted.org/mailman/listinfo/deltacloud-devel
