Class MCollective::RPC::ActionRunner
In: lib/mcollective/rpc/actionrunner.rb
Parent: Object

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

Methods

Attributes

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

Public Class methods

[Source]

    # File lib/mcollective/rpc/actionrunner.rb, line 26
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

[Source]

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

[Source]

    # File lib/mcollective/rpc/actionrunner.rb, line 91
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

[Source]

    # File lib/mcollective/rpc/actionrunner.rb, line 73
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

[Source]

     # File lib/mcollective/rpc/actionrunner.rb, line 129
129:       def path_to_command(command)
130:         unless command[0,1] == File::SEPARATOR
131:           Config.instance.libdir.each do |libdir|
132:             command_file = File.join(libdir, "agent", agent, command)
133: 
134:             return command_file if File.exist?(command_file)
135:           end
136:         end
137: 
138:         return command
139:       end

[Source]

    # File lib/mcollective/rpc/actionrunner.rb, line 36
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

[Source]

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

[Source]

     # File lib/mcollective/rpc/actionrunner.rb, line 99
 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

[Source]

    # File lib/mcollective/rpc/actionrunner.rb, line 66
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

[Source]

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

[Source]

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

[Validate]