3 # Class to wrap all the stats and to keep track of some timings
5 attr_accessor :noresponsefrom, :starttime, :discoverytime, :blocktime, :responses, :totaltime
6 attr_accessor :discovered, :discovered_nodes, :okcount, :failcount, :noresponsefrom, :responsesfrom
7 attr_accessor :requestid, :aggregate_summary, :ddl, :aggregate_failures
13 # Resets stats, if discovery time is set we keep it as it was
18 @starttime = Time.now.to_f
19 @discoverytime = 0 unless @discoverytime
23 @discovered_nodes = []
28 @aggregate_summary = []
29 @aggregate_failures = []
32 # returns a hash of our stats
34 {:noresponsefrom => @noresponsefrom,
35 :starttime => @starttime,
36 :discoverytime => @discoverytime,
37 :blocktime => @blocktime,
38 :responses => @responses,
39 :totaltime => @totaltime,
40 :discovered => @discovered,
41 :discovered_nodes => @discovered_nodes,
42 :noresponsefrom => @noresponsefrom,
44 :requestid => @requestid,
45 :failcount => @failcount,
46 :aggregate_summary => @aggregate_summary,
47 :aggregate_failures => @aggregate_failures}
50 # Fake hash access to keep things backward compatible
57 # increment the count of ok hosts
64 # increment the count of failed hosts
71 # Re-initializes the object with stats from the basic client
72 def client_stats=(stats)
73 @noresponsefrom = stats[:noresponsefrom]
74 @responses = stats[:responses]
75 @starttime = stats[:starttime]
76 @blocktime = stats[:blocktime]
77 @totaltime = stats[:totaltime]
78 @requestid = stats[:requestid]
79 @discoverytime = stats[:discoverytime] if @discoverytime == 0
82 # Utility to time discovery from :start to :end
83 def time_discovery(action)
85 @discovery_start = Time.now.to_f
87 @discoverytime = Time.now.to_f - @discovery_start
89 raise("Uknown discovery action #{action}")
95 # helper to time block execution time
96 def time_block_execution(action)
98 @block_start = Time.now.to_f
100 @blocktime += Time.now.to_f - @block_start
102 raise("Uknown block action #{action}")
108 # Update discovered and discovered_nodes based on
110 def discovered_agents(agents)
111 @discovered_nodes = agents
112 @discovered = agents.size
115 # Helper to calculate total time etc
117 @totaltime = @blocktime + @discoverytime
119 # figures out who we had no responses from
120 dhosts = @discovered_nodes.clone
121 @responsesfrom.each {|r| dhosts.delete(r)}
122 @noresponsefrom = dhosts
128 # Helper to keep track of who we received responses from
129 def node_responded(node)
130 @responsesfrom << node
132 @responsesfrom = [node]
135 def text_for_aggregates
136 result = StringIO.new
138 @aggregate_summary.each do |aggregate|
139 output_item = aggregate.result[:output]
142 action_interface = @ddl.action_interface(aggregate.action)
143 display_as = action_interface[:output][output_item][:display_as]
145 display_as = output_item
148 if aggregate.is_a?(Aggregate::Result::Base)
149 aggregate_report = aggregate.to_s
154 result.puts Util.colorize(:bold, "Summary of %s:" % display_as)
156 unless aggregate_report == ""
157 result.puts aggregate.to_s.split("\n").map{|x| " " + x}.join("\n")
159 result.puts Util.colorize(:yellow, " No aggregate summary could be computed")
164 @aggregate_failures.each do |failed|
167 message = "exception raised while processing startup hook"
169 message = "unspecified output '#{failed[:name]}' for the action"
171 message = "exception raised while processing result data"
173 message = "exception raised while summarizing"
176 result.puts Util.colorize(:bold, "Summary of %s:" % failed[:name])
178 result.puts Util.colorize(:yellow, " Could not compute summary - %s" % message)
185 # Returns a blob of text representing the request status based on the
186 # stats contained in this class
187 def report(caption = "rpc stats", summarize = true, verbose = false)
191 if @aggregate_summary.size > 0 && summarize
192 result_text << text_for_aggregates
197 result_text << Util.colorize(:yellow, "---- #{caption} ----")
200 @responses < @discovered ? color = :red : color = :reset
201 result_text << " Nodes: %s / %s" % [ Util.colorize(color, @discovered), Util.colorize(color, @responses) ]
203 result_text << " Nodes: #{@responses}"
206 @failcount < 0 ? color = :red : color = :reset
208 result_text << " Pass / Fail: %s / %s" % [Util.colorize(color, @okcount), Util.colorize(color, @failcount) ]
209 result_text << " Start Time: %s" % [Time.at(@starttime)]
210 result_text << " Discovery Time: %.2fms" % [@discoverytime * 1000]
211 result_text << " Agent Time: %.2fms" % [@blocktime * 1000]
212 result_text << " Total Time: %.2fms" % [@totaltime * 1000]
215 @responses < @discovered ? color = :red : color = :green
217 if @aggregate_summary.size + @aggregate_failures.size > 0 && summarize
218 result_text << text_for_aggregates
223 result_text << "Finished processing %s / %s hosts in %.2f ms" % [Util.colorize(color, @responses), Util.colorize(color, @discovered), @blocktime * 1000]
225 result_text << "Finished processing %s hosts in %.2f ms" % [Util.colorize(:bold, @responses), @blocktime * 1000]
229 if no_response_report != ""
230 result_text << "" << no_response_report
233 result_text.join("\n")
236 # Returns a blob of text indicating what nodes did not respond
237 def no_response_report
238 result_text = StringIO.new
240 if @noresponsefrom.size > 0
242 result_text.puts Util.colorize(:red, "No response from:")
245 @noresponsefrom.sort.in_groups_of(3) do |c|
246 result_text.puts " %-30s%-30s%-30s" % c