This Patch is Dependent on these patches/patch series Updated Data Service for All Quota Graphs - June 14th Added No Limits on Quota, force Quota Creation on Parent Create - June 14th
Thanks Martyn ----- Original Message ----- From: [email protected] To: [email protected] Cc: "martyntaylor" <[email protected]> Sent: Thursday, June 17, 2010 4:17:17 PM GMT +00:00 GMT Britain, Ireland, Portugal Subject: [PATCH aggregator] Generalized Quota Usage graph methods for creation of all Quota Usage Graphs From: martyntaylor <[email protected]> --- src/app/controllers/dashboard_controller.rb | 14 ++- src/app/models/graph.rb | 28 ++++ src/app/services/data_service.rb | 44 ++++--- src/app/services/graph_service.rb | 213 +++++++++++++++------------ src/app/views/dashboard/summary.haml | 2 +- 5 files changed, 185 insertions(+), 116 deletions(-) diff --git a/src/app/controllers/dashboard_controller.rb b/src/app/controllers/dashboard_controller.rb index bcd2073..f971728 100644 --- a/src/app/controllers/dashboard_controller.rb +++ b/src/app/controllers/dashboard_controller.rb @@ -39,9 +39,17 @@ class DashboardController < ApplicationController 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/data_service.rb b/src/app/services/data_service.rb index 776fef7..40968ef 100644 --- a/src/app/services/data_service.rb +++ b/src/app/services/data_service.rb @@ -62,24 +62,28 @@ class DataService # Returns the Used and Maximum Resource Usage def self.quota_utilisation(parent, resource_name) - quota = parent.quota - - case resource_name - when Quota::RESOURCE_RUNNING_INSTANCES - return QuotaUsagePoint.new(quota.running_instances, quota.maximum_running_instances) - when Quota::RESOURCE_RUNNING_MEMORY - return QuotaUsagePoint.new(quota.running_memory.to_f, quota.maximum_running_memory.to_f) - when Quota::RESOURCE_RUNNING_CPUS - return QuotaUsagePoint.new(quota.running_cpus.to_f, quota.maximum_running_cpus.to_f) - when Quota::RESOURCE_TOTAL_INSTANCES - return QuotaUsagePoint.new(quota.total_instances, quota.maximum_total_instances) - when Quota::RESOURCE_TOTAL_STORAGE - return QuotaUsagePoint.new(quota.total_storage.to_f, quota.maximum_total_storage.to_f) - when Quota::RESOURCE_OVERALL - return self.overall_usage(parent) - else - return nil + if parent + quota = parent.quota + if quota + case resource_name + when Quota::RESOURCE_RUNNING_INSTANCES + return QuotaUsagePoint.new(quota.running_instances, quota.maximum_running_instances) + when Quota::RESOURCE_RUNNING_MEMORY + return QuotaUsagePoint.new(quota.running_memory.to_f, quota.maximum_running_memory.to_f) + when Quota::RESOURCE_RUNNING_CPUS + return QuotaUsagePoint.new(quota.running_cpus.to_f, quota.maximum_running_cpus.to_f) + when Quota::RESOURCE_TOTAL_INSTANCES + return QuotaUsagePoint.new(quota.total_instances, quota.maximum_total_instances) + when Quota::RESOURCE_TOTAL_STORAGE + return QuotaUsagePoint.new(quota.total_storage.to_f, quota.maximum_total_storage.to_f) + when Quota::RESOURCE_OVERALL + return self.overall_usage(parent) + else + return nil + end + end end + return nil end def self.overall_usage(parent) @@ -91,7 +95,11 @@ class DataService worst_case = nil usage_points.each do |usage_point| if worst_case - if ((worst_case.max / 100) * worst_case.used) < ((usage_point.max / 100) * usage_point.used) + if worst_case.max == Quota::NO_LIMIT + worst_case = usage_point + elsif usage_point.max == Quota::NO_LIMIT + # DO Nothing + elsif ((worst_case.max / 100) * worst_case.used) < ((usage_point.max / 100) * usage_point.used) worst_case = usage_point end else diff --git a/src/app/services/graph_service.rb b/src/app/services/graph_service.rb index 11da01c..22e45f1 100644 --- a/src/app/services/graph_service.rb +++ b/src/app/services/graph_service.rb @@ -24,6 +24,15 @@ class GraphService graphs end + 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 (user,opts = {}) #FIXME add permission checks to filter what graphs user can get graphs = Hash.new @@ -58,104 +67,27 @@ class GraphService output_stream = IO::popen( cmd, "r+") end - def self.quota_instances_in_use_graph (cloud_account, opts = {}) - #things we're checking for in opts: :max_value, :height, :width - - unless max_value = opts[:max_value] - max_value = 100 unless max_value = Quota.maximum('maximum_running_instances') - end - 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| - 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.arbitrary_lines << "unset border" - - plot.set "bmargin","0" - plot.set "lmargin","0" - plot.set "rmargin","0" - plot.set "tmargin","0" - plot.set "boxwidth 0.9" - plot.set "style fill solid 1.0" - 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 - - - #The two arrays above are three columns of data for gnuplot. - plot.data << Gnuplot::DataSet.new( [[x[0]], [y[0]]] ) do |ds| - ds.using = "1:2" - ds.with = 'boxes linecolor rgb "#8cc63f"' - ds.notitle - end - - #The two arrays above are three columns of data for gnuplot. - plot.data << Gnuplot::DataSet.new( [[x[1]], [y[1]]] ) do |ds| - ds.using = "1:2" - ds.with = 'boxes linecolor rgb "#cccccc"' - ds.notitle - end - - - plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds| - ds.using = "1:2:2" - ds.with = "labels left offset 0,.15 rotate" - ds.notitle - end - end - gp.flush - gp.close_write - gp.read(nil,raw_svg) - gp.close_read - - #massage the svg so that the histogram's bars are oriented horizontally - xml = Nokogiri::XML(raw_svg) - - nodes = xml.root.children - wrapper = Nokogiri::XML::Node.new "g", xml.root - - nodes.each do |node| - node.parent = wrapper if node.name != "desc" && node.name != "defs" - end - - wrapper.parent = xml.root - wrapper.set_attribute 'transform',"translate(#{width},0) rotate(90) scale(#{height * 1.0 / width},#{width * 1.0/ height})" - - text_nodes = [] - xml.root.traverse do |node| - if node.name == 'text' && node.inner_text.strip =~ /^\d+$/ - if node.parent.name == 'g' - pparent = node.parent - text_nodes << pparent - end + 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 = DataService::quota_utilisation(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 - text_nodes.each do |node| - attr = node.get_attribute 'transform' - node.remove_attribute 'transform' - node.set_attribute 'transform',"#{attr} scale (#{height * 1.0 / width},#{width * 1.0/ height})" - end - - modified_svg = xml.to_s - graph = Graph.new - graph.svg = modified_svg - graph + 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 (provider, opts = {}) @@ -256,4 +188,97 @@ class GraphService 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] + max_value = 100 unless max_value = Quota.maximum('maximum_running_instances') + end + 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| + 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.arbitrary_lines << "unset border" + + plot.set "bmargin","0" + plot.set "lmargin","0" + plot.set "rmargin","0" + plot.set "tmargin","0" + plot.set "boxwidth 0.9" + plot.set "style fill solid 1.0" + 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 = 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| + ds.using = "1:2" + ds.with = 'boxes linecolor rgb "#8cc63f"' + ds.notitle + end + + #The two arrays above are three columns of data for gnuplot. + plot.data << Gnuplot::DataSet.new( [[x[1]], [y[1]]] ) do |ds| + ds.using = "1:2" + ds.with = 'boxes linecolor rgb "#cccccc"' + ds.notitle + end + + + plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds| + ds.using = "1:2:2" + ds.with = "labels left offset 0,.15 rotate" + ds.notitle + end + end + gp.flush + gp.close_write + gp.read(nil,raw_svg) + gp.close_read + + #massage the svg so that the histogram's bars are oriented horizontally + xml = Nokogiri::XML(raw_svg) + + nodes = xml.root.children + wrapper = Nokogiri::XML::Node.new "g", xml.root + + nodes.each do |node| + node.parent = wrapper if node.name != "desc" && node.name != "defs" + end + + wrapper.parent = xml.root + wrapper.set_attribute 'transform',"translate(#{width},0) rotate(90) scale(#{height * 1.0 / width},#{width * 1.0/ height})" + + text_nodes = [] + xml.root.traverse do |node| + if node.name == 'text' && node.inner_text.strip =~ /^\d+$/ + if node.parent.name == 'g' + pparent = node.parent + text_nodes << pparent + end + end + end + + text_nodes.each do |node| + attr = node.get_attribute 'transform' + node.remove_attribute 'transform' + node.set_attribute 'transform',"#{attr} scale (#{height * 1.0 / width},#{width * 1.0/ height})" + end + + modified_svg = xml.to_s + graph = Graph.new + graph.svg = modified_svg + graph + end + end diff --git a/src/app/views/dashboard/summary.haml b/src/app/views/dashboard/summary.haml index ccbe9aa..a0d6179 100644 --- a/src/app/views/dashboard/summary.haml +++ b/src/app/views/dashboard/summary.haml @@ -108,7 +108,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.6.6.1 _______________________________________________ deltacloud-devel mailing list [email protected] https://fedorahosted.org/mailman/listinfo/deltacloud-devel
