X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=lib%2Fmcollective%2Frpc%2Fstats.rb;fp=lib%2Fmcollective%2Frpc%2Fstats.rb;h=ae909d2a34801d817479b8f546b89d7c9c68bcc2;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/lib/mcollective/rpc/stats.rb b/lib/mcollective/rpc/stats.rb new file mode 100644 index 0000000..ae909d2 --- /dev/null +++ b/lib/mcollective/rpc/stats.rb @@ -0,0 +1,256 @@ +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