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