module MCollective module RPC # Class to wrap all the stats and to keep track of some timings class Stats attr_accessor :noresponsefrom, :starttime, :discoverytime, :blocktime, :responses, :totaltime attr_accessor :discovered, :discovered_nodes, :okcount, :failcount, :noresponsefrom, :responsesfrom attr_accessor :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 = [] @responsesfrom = [] @responses = 0 @starttime = Time.now.to_f @discoverytime = 0 unless @discoverytime @blocktime = 0 @totaltime = 0 @discovered = 0 @discovered_nodes = [] @okcount = 0 @failcount = 0 @noresponsefrom = [] @requestid = nil @aggregate_summary = [] @aggregate_failures = [] end # returns a hash of our stats def to_hash {:noresponsefrom => @noresponsefrom, :starttime => @starttime, :discoverytime => @discoverytime, :blocktime => @blocktime, :responses => @responses, :totaltime => @totaltime, :discovered => @discovered, :discovered_nodes => @discovered_nodes, :noresponsefrom => @noresponsefrom, :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] @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 # figures out who we had no responses from dhosts = @discovered_nodes.clone @responsesfrom.each {|r| dhosts.delete(r)} @noresponsefrom = dhosts rescue @totaltime = 0 @noresponsefrom = [] 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 if no_response_report != "" result_text << "" << no_response_report 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 result_text.puts Util.colorize(:red, "No response from:") result_text.puts @noresponsefrom.sort.in_groups_of(3) do |c| result_text.puts " %-30s%-30s%-30s" % c end result_text.puts end result_text.string end end end end