class MCollective::RPC::Helpers - mcollective version 2.12.5

# class MCollective::RPC::Helpers

Various utilities for the `RPC` system

### Public Class Methods

add_simplerpc_options(parser, options) click to toggle source

Add SimpleRPC common options

        # File lib/mcollective/rpc/helpers.rb
    264 def self.add_simplerpc_options(parser, options)
    265   parser.separator ""
    266   parser.separator "RPC Options"
    267
    268   # add SimpleRPC specific options to all clients that use our library
    269   parser.on('--np', '--no-progress', 'Do not show the progress bar') do |v|
    270     options[:progress_bar] = false
    271   end
    272
    273   parser.on('--one', '-1', 'Send request to only one discovered nodes') do |v|
    274     options[:mcollective_limit_targets] = 1
    275   end
    276
    277   parser.on('--batch SIZE', 'Do requests in batches') do |v|
    278     # validate batch string. Is it x% where x > 0 or is it an integer
    279     if ((v =~ /^(\d+)%$/ && Integer($1) != 0) || v =~ /^(\d+)$/)
    280       options[:batch_size] = v
    281     else
    282       raise(::OptionParser::InvalidArgument.new(v))
    283     end
    284   end
    285
    286   parser.on('--batch-sleep SECONDS', Float, 'Sleep time between batches') do |v|
    287     options[:batch_sleep_time] = v
    288   end
    289
    290   parser.on('--limit-seed NUMBER', Integer, 'Seed value for deterministic random batching') do |v|
    291     options[:limit_seed] = v
    292   end
    293
    294   parser.on('--limit-nodes COUNT', '--ln', '--limit', 'Send request to only a subset of nodes, can be a percentage') do |v|
    295     raise "Invalid limit specified: #{v} valid limits are /^\d+%*$/" unless v =~ /^\d+%*$/
    296
    297     if v =~ /^\d+$/
    298       options[:mcollective_limit_targets] = v.to_i
    299     else
    300       options[:mcollective_limit_targets] = v
    301     end
    302   end
    303
    304   parser.on('--json', '-j', 'Produce JSON output') do |v|
    305     options[:progress_bar] = false
    306     options[:output_format] = :json
    307   end
    308
    309   parser.on('--display MODE', 'Influence how results are displayed. One of ok, all or failed') do |v|
    310     if v == "all"
    311       options[:force_display_mode] = :always
    312     else
    313       options[:force_display_mode] = v.intern
    314     end
    315
    316     raise "--display has to be one of 'ok', 'all' or 'failed'" unless [:ok, :failed, :always].include?(options[:force_display_mode])
    317   end
    318 end

extract_hosts_from_array(hosts) click to toggle source

Given an array of something, make sure each is a string chomp off any new lines and return just the array of hosts

       # File lib/mcollective/rpc/helpers.rb
    39 def self.extract_hosts_from_array(hosts)
    40   [hosts].flatten.map do |host|
    41     raise "#{host} should be a string" unless host.is_a?(String)
    42     host.chomp
    43   end
    44 end

extract_hosts_from_json(json) click to toggle source

Parse JSON output as produced by printrpc or puppet query and extract the “sender” / “certname” of each entry

The simplist valid JSON based data would be:

[

    {"sender" => "example.com"},
    {"sender" => "another.com"}

]

or

[

    {"certname" => "example.com"},
    {"certname" => "another.com"}

]

       # File lib/mcollective/rpc/helpers.rb
    21 def self.extract_hosts_from_json(json)
    22   hosts = JSON.parse(json)
    23
    24   raise "JSON hosts list is not an array" unless hosts.is_a?(Array)
    25
    26   hosts.map do |host|
    27     raise "JSON host list is not an array of Hashes" unless host.is_a?(Hash)
    28
    29     unless host.include?("sender") || host.include?("certname")
    30       raise "JSON host list does not have senders in it"
    31     end
    32
    33     host["sender"] || host["certname"]
    34   end.uniq
    35 end

old_rpcresults(result, flags = {}) click to toggle source

Backward compatible display block for results without a `DDL`

        # File lib/mcollective/rpc/helpers.rb
    213 def self.old_rpcresults(result, flags = {})
    214   result_text = ""
    215
    216   if flags[:flatten]
    217     result.each do |r|
    218       if r[:statuscode] <= 1
    219         data = r[:data]
    220
    221         unless data.is_a?(String)
    222           result_text << data.pretty_inspect
    223         else
    224           result_text << data
    225         end
    226       else
    227         result_text << r.pretty_inspect
    228       end
    229     end
    230
    231     result_text << ""
    232   else
    233     [result].flatten.each do |r|
    234       if flags[:verbose]
    235         result_text << "%-40s: %s\n" % [r[:sender], r[:statusmsg]]
    236
    237         if r[:statuscode] <= 1
    238           r[:data].pretty_inspect.split("\n").each {|m| result_text += "    #{m}"}
    239           result_text << "\n\n"
    240         elsif r[:statuscode] == 2
    241           # dont print anything, no useful data to display
    242           # past what was already shown
    243         elsif r[:statuscode] == 3
    244           # dont print anything, no useful data to display
    245           # past what was already shown
    246         elsif r[:statuscode] == 4
    247           # dont print anything, no useful data to display
    248           # past what was already shown
    249         else
    250           result_text << "    #{r[:statusmsg]}"
    251         end
    252       else
    253         unless r[:statuscode] == 0
    254           result_text << "%-40s %s\n" % [r[:sender], Util.colorize(:red, r[:statusmsg])]
    255         end
    256       end
    257     end
    258   end
    259
    260   result_text << ""
    261 end

rpcresults(result, flags = {}) click to toggle source

Returns a blob of text representing the results in a standard way

It tries hard to do sane things so you often should not need to write your own display functions

If the agent you are getting results for has a `DDL` it will use the hints in there to do the right thing specifically it will look at the values of display in the `DDL` to choose when to show results

If you do not have a `DDL` you can pass these flags:

    printrpc exim.mailq, :flatten => true
    printrpc exim.mailq, :verbose => true

If you've asked it to flatten the result it will not print sender hostnames, it will just print the result as if it's one huge result, handy for things like showing a combined mailq.

        # File lib/mcollective/rpc/helpers.rb
     64 def self.rpcresults(result, flags = {})
     65   flags = {:verbose => false, :flatten => false, :format => :console, :force_display_mode => false}.merge(flags)
     66
     67   result_text = ""
     68   ddl = nil
     69
     70   # if running in verbose mode, just use the old style print
     71   # no need for all the DDL helpers obfuscating the result
     72   if flags[:format] == :json
     73     if STDOUT.tty?
     74       result_text = JSON.pretty_generate(result)
     75     else
     76       result_text = result.to_json
     77     end
     78   else
     79     if flags[:verbose]
     80       result_text = old_rpcresults(result, flags)
     81     else
     82       [result].flatten.each do |r|
     83         begin
     84           ddl ||= DDL.new(r.agent).action_interface(r.action.to_s)
     85
     86           sender = r[:sender]
     87           status = r[:statuscode]
     88           message = r[:statusmsg]
     89           result = r[:data]
     90
     91           if flags[:force_display_mode]
     92             display = flags[:force_display_mode]
     93           else
     94             display = ddl[:display]
     95           end
     96
     97           # appand the results only according to what the DDL says
     98           case display
     99             when :ok
    100               if status == 0
    101                 result_text << text_for_result(sender, status, message, result, ddl)
    102               end
    103
    104             when :failed
    105               if status > 0
    106                 result_text << text_for_result(sender, status, message, result, ddl)
    107               end
    108
    109             when :always
    110               result_text << text_for_result(sender, status, message, result, ddl)
    111
    112             when :flatten
    113               Log.warn("The display option :flatten is being deprecated and will be removed in the next minor release")
    114               result_text << text_for_flattened_result(status, result)
    115
    116           end
    117         rescue Exception => e
    118           # no DDL so just do the old style print unchanged for
    119           # backward compat
    120           result_text = old_rpcresults(result, flags)
    121         end
    122       end
    123     end
    124   end
    125
    126   result_text
    127 end

text_for_flattened_result(status, result) click to toggle source

Returns text representing a flattened result of only good data

        # File lib/mcollective/rpc/helpers.rb
    200 def self.text_for_flattened_result(status, result)
    201   result_text = ""
    202
    203   if status <= 1
    204     unless result.is_a?(String)
    205       result_text << result.pretty_inspect
    206     else
    207       result_text << result
    208     end
    209   end
    210 end

text_for_result(sender, status, msg, result, ddl) click to toggle source

Return text representing a result

        # File lib/mcollective/rpc/helpers.rb
    130 def self.text_for_result(sender, status, msg, result, ddl)
    131   statusses = ["",
    132                Util.colorize(:red, "Request Aborted"),
    133                Util.colorize(:yellow, "Unknown Action"),
    134                Util.colorize(:yellow, "Missing Request Data"),
    135                Util.colorize(:yellow, "Invalid Request Data"),
    136                Util.colorize(:red, "Unknown Request Status")]
    137
    138   result_text = "%-40s %s\n" % [sender, statusses[status]]
    139   result_text << "   %s\n" % [Util.colorize(:yellow, msg)] unless msg == "OK"
    140
    141   # only print good data, ignore data that results from failure
    142   if status == 0
    143     if result.is_a?(Hash)
    144       # figure out the lengths of the display as strings, we'll use
    145       # it later to correctly justify the output
    146       lengths = result.keys.map do |k|
    147         begin
    148           ddl[:output][k][:display_as].size
    149         rescue
    150           k.to_s.size
    151         end
    152       end
    153
    154       result.keys.sort_by{|k| k}.each do |k|
    155         # get all the output fields nicely lined up with a
    156         # 3 space front padding
    157         begin
    158           display_as = ddl[:output][k][:display_as]
    159         rescue
    160           display_as = k.to_s
    161         end
    162
    163         display_length = display_as.size
    164         padding = lengths.max - display_length + 3
    165         result_text << " " * padding
    166
    167         result_text << "#{display_as}:"
    168
    169         if [String, Numeric].include?(result[k].class)
    170           lines = result[k].to_s.split("\n")
    171
    172           if lines.empty?
    173             result_text << "\n"
    174           else
    175             lines.each_with_index do |line, i|
    176               i == 0 ? padtxt = " " : padtxt = " " * (padding + display_length + 2)
    177
    178               result_text << "#{padtxt}#{line}\n"
    179             end
    180           end
    181         else
    182           padding = " " * (lengths.max + 5)
    183           result_text << " " << result[k].pretty_inspect.split("\n").join("\n" << padding) << "\n"
    184         end
    185       end
    186     elsif status == 1
    187       # for status 1 we dont want to show half baked
    188       # data by default since the DDL will supply all the defaults
    189       # it just doesnt look right
    190     else
    191       result_text << "\n\t" + result.pretty_inspect.split("\n").join("\n\t")
    192     end
    193   end
    194
    195   result_text << "\n"
    196   result_text
    197 end