ACK, looks good, works for me
-Mo
On 06/06/2010 05:48 PM, Steve Linabery wrote:
> Create GraphService object to manage calls to forked gnuplot process
> for chart generation. Also add Graph DTO for encapsulating svg or various
> other chart description languages.
> ---
> deltacloud-aggregator.spec.in | 1 +
> src/app/models/graph.rb | 9 ++
> src/app/services/graph_service.rb | 186
> +++++++++++++++++++++++++++++++++++++
> src/config/environment.rb | 1 +
> 4 files changed, 197 insertions(+), 0 deletions(-)
> create mode 100644 src/app/models/graph.rb
> create mode 100644 src/app/services/graph_service.rb
>
> diff --git a/deltacloud-aggregator.spec.in b/deltacloud-aggregator.spec.in
> index 6f91751..88ed70e 100644
> --- a/deltacloud-aggregator.spec.in
> +++ b/deltacloud-aggregator.spec.in
> @@ -28,6 +28,7 @@ Requires: rubygem(will_paginate)
> Requires: postgresql
> Requires: postgresql-server
> Requires: ruby-postgres
> +Requires: gnuplot>= 4.2.6
>
> %package daemons
> Summary: Deltacloud Aggregator daemons config
> diff --git a/src/app/models/graph.rb b/src/app/models/graph.rb
> new file mode 100644
> index 0000000..f520a82
> --- /dev/null
> +++ b/src/app/models/graph.rb
> @@ -0,0 +1,9 @@
> +class Graph
> + attr_accessor :svg
> +
> + QOS_AVG_TIME_TO_SUBMIT = "qos_avg_time_to_submit"
> + QUOTA_INSTANCES_IN_USE = "quota_instances_in_use"
> + def initialize
> + @svg = ""
> + end
> +end
> diff --git a/src/app/services/graph_service.rb
> b/src/app/services/graph_service.rb
> new file mode 100644
> index 0000000..9a2d12a
> --- /dev/null
> +++ b/src/app/services/graph_service.rb
> @@ -0,0 +1,186 @@
> +class GraphService
> + require 'gnuplot'
> + require 'nokogiri'
> +
> + def self.dashboard_quota (user,opts = {})
> + #FIXME add permission checks to filter what graphs user can get
> + graphs = Hash.new
> +
> + #if a specific cloud account is given, just return that cloud account's
> graph.
> + #otherwise return all graphs user has permission to see.
> + 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)
> + graphs[cloud_account] = cloud_account_graphs
> + else
> + CloudAccount.all.each do |cloud_account|
> + cloud_account_graphs = Hash.new
> + cloud_account_graphs[Graph::QUOTA_INSTANCES_IN_USE] =
> quota_instances_in_use_graph(cloud_account,opts)
> + graphs[cloud_account] = cloud_account_graphs
> + end
> + end
> + graphs
> + end
> +
> + def self.dashboard_qos (user,opts = {})
> + #FIXME add permission checks to filter what graphs user can get
> + graphs = Hash.new
> +
> + #if a specific provider is given, just return that provider's graph.
> + #otherwise return all graphs user has permission to see.
> + if opts[:provider]
> + provider = opts[:provider]
> + provider_graphs = Hash.new
> + provider_graphs[Graph::QOS_AVG_TIME_TO_SUBMIT] =
> qos_avg_time_to_submit_graph(provider,opts)
> + graphs[provider] = provider_graphs
> + else
> + Provider.all.each do |provider|
> + provider_graphs = Hash.new
> + provider_graphs[Graph::QOS_AVG_TIME_TO_SUBMIT] =
> qos_avg_time_to_submit_graph(provider,opts)
> + graphs[provider] = provider_graphs
> + end
> + end
> + graphs
> + end
> +
> + private
> + def self.gnuplot_open( persist=false )
> + cmd = Gnuplot.gnuplot( persist ) or raise 'gnuplot not found'
> + 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 height = opts[:height]
> + width = 150 unless width = opts[:width]
> +
> + 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 relative"
> + plot.set "style fill solid 1.0"
> + plot.set "xrange [.25:2.75]"
> + plot.set "yrange [0:#{max_value * 1.25}]" #we want to scale 25% 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, y] ) do |ds|
> + ds.using = "1:2"
> + ds.with = "boxes"
> + 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
> +
> + def self.qos_avg_time_to_submit_graph (provider, opts = {})
> + #things we're checking for in opts: :height, :width
> +
> + height = 60 unless height = opts[:height]
> + width = 100 unless width = opts[:width]
> +
> + 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","0"
> + 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
> +
> +end
> diff --git a/src/config/environment.rb b/src/config/environment.rb
> index 5efa7bb..53ed761 100644
> --- a/src/config/environment.rb
> +++ b/src/config/environment.rb
> @@ -47,6 +47,7 @@ Rails::Initializer.run do |config|
> config.gem "haml"
> config.gem "will_paginate"
> config.gem "nokogiri", :version => ">= 1.4.0"
> + config.gem "gnuplot"
>
> config.active_record.observers = :instance_observer, :task_observer
> # Only load the plugins named here, in the order given. By default, all
> plugins
>
_______________________________________________
deltacloud-devel mailing list
[email protected]
https://fedorahosted.org/mailman/listinfo/deltacloud-devel