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

Reply via email to