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

# class MCollective::RPC::ActionRunner

A helper used by RPC::Agent#implemented_by to delegate an action to an external script. At present only JSON based serialization is supported in future ones based on key=val pairs etc will be added

It serializes the request object into an input file and creates an empty output file. It then calls the external command reading the output file at the end.

any STDERR gets logged at error level and any STDOUT gets logged at info level.

It will interpret the exit code from the application the same way `RPC::Reply#fail!` and fail handles their codes creating a consistent interface, the message part of the fail message will come from STDERR

Generally externals should just exit with code 1 on failure and print to STDERR, this is exactly what Perl die() does and translates perfectly to our model

It uses the `MCollective::Shell` wrapper to call the external application

### Attributes

action[R]
agent[R]
command[R]
format[R]
request[R]
stderr[R]
stdout[R]

### Public Class Methods

new(command, request, format=:json) click to toggle source

       # File lib/mcollective/rpc/actionrunner.rb
    26 def initialize(command, request, format=:json)
    27   @agent = request.agent
    28   @action = request.action
    29   @format = format
    30   @request = request
    31   @command = path_to_command(command)
    32   @stdout = ""
    33   @stderr = ""
    34 end

### Public Instance Methods

canrun?(command) click to toggle source

        # File lib/mcollective/rpc/actionrunner.rb
    117 def canrun?(command)
    118   File.executable?(command)
    119 end

load_json_results(file) click to toggle source

       # File lib/mcollective/rpc/actionrunner.rb
    91 def load_json_results(file)
    92   return {} unless File.readable?(file)
    93
    94   JSON.load(File.read(file)) || {}
    95 rescue JSON::ParserError
    96   {}
    97 end

load_results(file) click to toggle source

       # File lib/mcollective/rpc/actionrunner.rb
    73 def load_results(file)
    74   Log.debug("Attempting to load results in #{format} format from #{file}")
    75
    76   data = {}
    77
    78   if respond_to?("load_#{format}_results")
    79     tempdata = send("load_#{format}_results", file)
    80
    81     tempdata.each_pair do |k,v|
    82       data[k.to_sym] = v
    83     end
    84   end
    85
    86   data
    87 rescue Exception => e
    88   {}
    89 end

path_to_command(command) click to toggle source

        # File lib/mcollective/rpc/actionrunner.rb
    129 def path_to_command(command)
    130   if Util.absolute_path?(command)
    131     return command
    132   end
    133
    134   Config.instance.libdir.each do |libdir|
    135     command_file_old = File.join(libdir, "agent", agent, command)
    136     command_file_new = File.join(libdir, "mcollective", "agent", agent, command)
    137     command_file_old_exists = File.exists?(command_file_old)
    138     command_file_new_exists = File.exists?(command_file_new)
    139
    140     if command_file_new_exists && command_file_old_exists
    141       Log.debug("Found 'implemented_by' scripts found in two locations #{command_file_old} and #{command_file_new}")
    142       Log.debug("Running script: #{command_file_new}")
    143       return command_file_new
    144     elsif command_file_old_exists
    145       Log.debug("Running script: #{command_file_old}")
    146       return command_file_old
    147     elsif command_file_new_exists
    148       Log.debug("Running script: #{command_file_new}")
    149       return command_file_new
    150     end
    151   end
    152
    153   Log.warn("No script found for: #{command}")
    154   command
    155 end

run() click to toggle source

       # File lib/mcollective/rpc/actionrunner.rb
    36 def run
    37   unless canrun?(command)
    38     Log.warn("Cannot run #{to_s}")
    39     raise RPCAborted, "Cannot execute #{to_s}"
    40   end
    41
    42   Log.debug("Running #{to_s}")
    43
    44   request_file = saverequest(request)
    45   reply_file = tempfile("reply")
    46   reply_file.close
    47
    48   runner = shell(command, request_file.path, reply_file.path)
    49
    50   runner.runcommand
    51
    52   Log.debug("#{command} exited with #{runner.status.exitstatus}")
    53
    54   stderr.each_line {|l| Log.error("#{to_s}: #{l.chomp}")} unless stderr.empty?
    55   stdout.each_line {|l| Log.info("#{to_s}: #{l.chomp}")} unless stdout.empty?
    56
    57   {:exitstatus => runner.status.exitstatus,
    58    :stdout     => runner.stdout,
    59    :stderr     => runner.stderr,
    60    :data       => load_results(reply_file.path)}
    61 ensure
    62   request_file.close! if request_file.respond_to?("close!")
    63   reply_file.close! if reply_file.respond_to?("close")
    64 end

save_json_request(req) click to toggle source

        # File lib/mcollective/rpc/actionrunner.rb
    113 def save_json_request(req)
    114   req.to_json
    115 end

saverequest(req) click to toggle source

        # File lib/mcollective/rpc/actionrunner.rb
     99 def saverequest(req)
    100   Log.debug("Attempting to save request in #{format} format")
    101
    102   if respond_to?("save_#{format}_request")
    103     data = send("save_#{format}_request", req)
    104
    105     request_file = tempfile("request")
    106     request_file.puts data
    107     request_file.close
    108   end
    109
    110   request_file
    111 end

shell(command, infile, outfile) click to toggle source

       # File lib/mcollective/rpc/actionrunner.rb
    66 def shell(command, infile, outfile)
    67   env = {"MCOLLECTIVE_REQUEST_FILE" => infile,
    68          "MCOLLECTIVE_REPLY_FILE"   => outfile}
    69
    70   Shell.new("#{command} #{infile} #{outfile}", :cwd => Dir.tmpdir, :stdout => stdout, :stderr => stderr, :environment => env)
    71 end

tempfile(prefix) click to toggle source

        # File lib/mcollective/rpc/actionrunner.rb
    125 def tempfile(prefix)
    126   Tempfile.new("mcollective_#{prefix}", Dir.tmpdir)
    127 end

to_s() click to toggle source

        # File lib/mcollective/rpc/actionrunner.rb
    121 def to_s
    122   "%s#%s command: %s" % [ agent, action, command ]
    123 end