File: //proc/thread-self/root/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/mcollective/rpc/stats.rb
module MCollective
module RPC
# Class to wrap all the stats and to keep track of some timings
class Stats
attr_accessor :noresponsefrom, :unexpectedresponsefrom, :starttime, :discoverytime, :blocktime, :responses
attr_accessor :totaltime, :discovered, :discovered_nodes, :okcount, :failcount, :noresponsefrom
attr_accessor :responsesfrom, :requestid, :aggregate_summary, :ddl, :aggregate_failures
def initialize
reset
end
# Resets stats, if discovery time is set we keep it as it was
def reset
@noresponsefrom = []
@unexpectedresponsefrom = []
@responsesfrom = []
@responses = 0
@starttime = Time.now.to_f
@discoverytime = 0 unless @discoverytime
@blocktime = 0
@totaltime = 0
@discovered = 0
@discovered_nodes = []
@okcount = 0
@failcount = 0
@requestid = nil
@aggregate_summary = []
@aggregate_failures = []
end
# returns a hash of our stats
def to_hash
{:noresponsefrom => @noresponsefrom,
:unexpectedresponsefrom => @unexpectedresponsefrom,
:starttime => @starttime,
:discoverytime => @discoverytime,
:blocktime => @blocktime,
:responses => @responses,
:totaltime => @totaltime,
:discovered => @discovered,
:discovered_nodes => @discovered_nodes,
:okcount => @okcount,
:requestid => @requestid,
:failcount => @failcount,
:aggregate_summary => @aggregate_summary,
:aggregate_failures => @aggregate_failures}
end
# Fake hash access to keep things backward compatible
def [](key)
to_hash[key]
rescue
nil
end
# increment the count of ok hosts
def ok
@okcount += 1
rescue
@okcount = 1
end
# increment the count of failed hosts
def fail
@failcount += 1
rescue
@failcount = 1
end
# Re-initializes the object with stats from the basic client
def client_stats=(stats)
@noresponsefrom = stats[:noresponsefrom]
@unexpectedresponsefrom = stats[:unexpectedresponsefrom]
@responses = stats[:responses]
@starttime = stats[:starttime]
@blocktime = stats[:blocktime]
@totaltime = stats[:totaltime]
@requestid = stats[:requestid]
@discoverytime = stats[:discoverytime] if @discoverytime == 0
end
# Utility to time discovery from :start to :end
def time_discovery(action)
if action == :start
@discovery_start = Time.now.to_f
elsif action == :end
@discoverytime = Time.now.to_f - @discovery_start
else
raise("Uknown discovery action #{action}")
end
rescue
@discoverytime = 0
end
# helper to time block execution time
def time_block_execution(action)
if action == :start
@block_start = Time.now.to_f
elsif action == :end
@blocktime += Time.now.to_f - @block_start
else
raise("Uknown block action #{action}")
end
rescue
@blocktime = 0
end
# Update discovered and discovered_nodes based on
# discovery results
def discovered_agents(agents)
@discovered_nodes = agents
@discovered = agents.size
end
# Helper to calculate total time etc
def finish_request
@totaltime = @blocktime + @discoverytime
# figure out who responded unexpectedly
@unexpectedresponsefrom = @responsesfrom - @discovered_nodes
# figure out who we had no responses from
@noresponsefrom = @discovered_nodes - @responsesfrom
rescue
@totaltime = 0
@noresponsefrom = []
@unexpectedresponsefrom = []
end
# Helper to keep track of who we received responses from
def node_responded(node)
@responsesfrom << node
rescue
@responsesfrom = [node]
end
def text_for_aggregates
result = StringIO.new
@aggregate_summary.each do |aggregate|
output_item = aggregate.result[:output]
begin
action_interface = @ddl.action_interface(aggregate.action)
display_as = action_interface[:output][output_item][:display_as]
rescue
display_as = output_item
end
if aggregate.is_a?(Aggregate::Result::Base)
aggregate_report = aggregate.to_s
else
next
end
result.puts Util.colorize(:bold, "Summary of %s:" % display_as)
result.puts
unless aggregate_report == ""
result.puts aggregate.to_s.split("\n").map{|x| " " + x}.join("\n")
else
result.puts Util.colorize(:yellow, " No aggregate summary could be computed")
end
result.puts
end
@aggregate_failures.each do |failed|
case(failed[:type])
when :startup
message = "exception raised while processing startup hook"
when :create
message = "unspecified output '#{failed[:name]}' for the action"
when :process_result
message = "exception raised while processing result data"
when :summarize
message = "exception raised while summarizing"
end
result.puts Util.colorize(:bold, "Summary of %s:" % failed[:name])
result.puts
result.puts Util.colorize(:yellow, " Could not compute summary - %s" % message)
result.puts
end
result.string
end
# Returns a blob of text representing the request status based on the
# stats contained in this class
def report(caption = "rpc stats", summarize = true, verbose = false)
result_text = []
if verbose
if @aggregate_summary.size > 0 && summarize
result_text << text_for_aggregates
else
result_text << ""
end
result_text << Util.colorize(:yellow, "---- #{caption} ----")
if @discovered
@responses < @discovered ? color = :red : color = :reset
result_text << " Nodes: %s / %s" % [ Util.colorize(color, @discovered), Util.colorize(color, @responses) ]
else
result_text << " Nodes: #{@responses}"
end
@failcount < 0 ? color = :red : color = :reset
result_text << " Pass / Fail: %s / %s" % [Util.colorize(color, @okcount), Util.colorize(color, @failcount) ]
result_text << " Start Time: %s" % [Time.at(@starttime)]
result_text << " Discovery Time: %.2fms" % [@discoverytime * 1000]
result_text << " Agent Time: %.2fms" % [@blocktime * 1000]
result_text << " Total Time: %.2fms" % [@totaltime * 1000]
else
if @discovered
@responses < @discovered ? color = :red : color = :green
if @aggregate_summary.size + @aggregate_failures.size > 0 && summarize
result_text << text_for_aggregates
else
result_text << ""
end
result_text << "Finished processing %s / %s hosts in %.2f ms" % [Util.colorize(color, @responses), Util.colorize(color, @discovered), @blocktime * 1000]
else
result_text << "Finished processing %s hosts in %.2f ms" % [Util.colorize(:bold, @responses), @blocktime * 1000]
end
end
no_response_r = no_response_report
unexpected_response_r = unexpected_response_report
if no_response_r || unexpected_response_r
result_text << ""
end
if no_response_r != ""
result_text << "" << no_response_r
end
if unexpected_response_r != ""
result_text << "" << unexpected_response_r
end
if no_response_r || unexpected_response_r
result_text << ""
end
result_text.join("\n")
end
# Returns a blob of text indicating what nodes did not respond
def no_response_report
result_text = StringIO.new
if @noresponsefrom.size > 0
result_text.puts Util.colorize(:red, "No response from:")
result_text.puts
field_size = Util.field_size(@noresponsefrom, 30)
fields_num = Util.field_number(field_size)
format = " " + ( " %-#{field_size}s" * fields_num )
@noresponsefrom.sort.in_groups_of(fields_num) do |c|
result_text.puts format % c
end
end
result_text.string
end
# Returns a blob of text indicating what nodes responded but weren't discovered
def unexpected_response_report
result_text = StringIO.new
if @unexpectedresponsefrom.size > 0
result_text.puts Util.colorize(:red, "Unexpected response from:")
result_text.puts
field_size = Util.field_size(@unexpectedresponsefrom, 30)
fields_num = Util.field_number(field_size)
format = " " + ( " %-#{field_size}s" * fields_num )
@unexpectedresponsefrom.sort.in_groups_of(fields_num) do |c|
result_text.puts format % c
end
end
result_text.string
end
end
end
end