A wrapper around the traditional agent, it takes care of a lot of the tedious setup you would do for each agent allowing you to just create methods following a naming standard leaving the heavy lifting up to this clas.
See marionette-collective.org/simplerpc/agents.html
It only really makes sense to use this with a Simple RPC client on the other end, basic usage would be:
module MCollective module Agent class Helloworld<RPC::Agent action "hello" do reply[:msg] = "Hello #{request[:name]}" end action "foo" do implemented_by "/some/script.sh" end end end end
If you wish to implement the logic for an action using an external script use the implemented_by method that will cause your script to be run with 2 arguments.
The first argument is a file containing JSON with the request and the 2nd argument is where the script should save its output as a JSON hash.
We also currently have the validation code in here, this will be moved to plugins soon.
Returns an array of actions this agent support
# File lib/mcollective/rpc/agent.rb, line 157 def self.actions public_instance_methods.sort.grep(%r_action$/).map do |method| $1 if method =~ %r(.+)_action$/ end end
By default RPC Agents support a toggle in the configuration that can enable and disable them based on the agent name
Example an agent called Foo can have:
plugin.foo.activate_agent = false
and this will prevent the agent from loading on this particular machine.
Agents can use the activate_when helper to override this for example:
activate_when do
File.exist?("/usr/bin/puppet")
end
# File lib/mcollective/rpc/agent.rb, line 142 def self.activate? agent_name = self.to_s.split("::").last.downcase log_code(:PLMC37, "Starting default activation checks for the '%{agent}' agent", :debug, :agent => agent_name) should_activate = Util.str_to_bool(Config.instance.pluginconf.fetch("#{agent_name}.activate_agent", true)) unless should_activate log_code(:PLMC38, "Found plugin configuration '%{agent}.activate_agent' with value '%{should_activate}'", :debug, :agent => agent_name, :should_activate => should_activate) end return should_activate end
# File lib/mcollective/rpc/agent.rb, line 40 def initialize @agent_name = self.class.to_s.split("::").last.downcase load_ddl @logger = Log.instance @config = Config.instance # if we have a global authorization provider enable it # plugins can still override it per plugin self.class.authorized_by(@config.rpcauthprovider) if @config.rpcauthorization startup_hook end
# File lib/mcollective/rpc/agent.rb, line 64 def handlemsg(msg, connection) @request = RPC::Request.new(msg, @ddl) @reply = RPC::Reply.new(@request.action, @ddl) begin # Incoming requests need to be validated against the DDL thus reusing # all the work users put into creating DDLs and creating a consistent # quality of input validation everywhere with the a simple once off # investment of writing a DDL @request.validate! # Calls the authorization plugin if any is defined # if this raises an exception we wil just skip processing this # message authorization_hook(@request) if respond_to?("authorization_hook") # Audits the request, currently continues processing the message # we should make this a configurable so that an audit failure means # a message wont be processed by this node depending on config audit_request(@request, connection) before_processing_hook(msg, connection) if respond_to?("#{@request.action}_action") send("#{@request.action}_action") else log_code(:PLMC36, "Unknown action '%{action}' for agent '%{agent}'", :warn, :action => @request.action, :agent => @request.agent) raise UnknownRPCAction, "Unknown action '#{@request.action}' for agent '#{@request.agent}'" end rescue RPCAborted => e @reply.fail e.to_s, 1 rescue UnknownRPCAction => e @reply.fail e.to_s, 2 rescue MissingRPCData => e @reply.fail e.to_s, 3 rescue InvalidRPCData, DDLValidationError => e @reply.fail e.to_s, 4 rescue UnknownRPCError => e Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s]) Log.error(e.backtrace.join("\n\t")) @reply.fail e.to_s, 5 rescue Exception => e Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s]) Log.error(e.backtrace.join("\n\t")) @reply.fail e.to_s, 5 end after_processing_hook if @request.should_respond? return @reply.to_hash else log_code(:PLMC35, "Client did not request a response, surpressing reply", :debug) return nil end end
# File lib/mcollective/rpc/agent.rb, line 55 def load_ddl @ddl = DDL.new(@agent_name, :agent) @meta = @ddl.meta @timeout = @meta[:timeout] || 10 rescue Exception => e DDL.validation_fail!(:PLMC24, "Failed to load DDL for the '%{agent}' agent, DDLs are required: %{error_class}: %{error}", :error, :agent => @agent_name, :error_class => e.class, :error => e.to_s) end