class MCollective::RPC::Agent

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 docs.puppetlabs.com/mcollective/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.

Attributes

agent_name[RW]
config[R]
ddl[R]
logger[R]
meta[R]
reply[RW]
request[RW]
timeout[R]

Public Class Methods

actions() click to toggle source

Returns an array of actions this agent support

    # File lib/mcollective/rpc/agent.rb
160 def self.actions
161   public_instance_methods.sort.grep(/_action$/).map do |method|
162     $1 if method =~ /(.+)_action$/
163   end
164 end
activate?() click to toggle source

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
139 def self.activate?
140   agent_name = self.to_s.split("::").last.downcase
141   config = Config.instance
142 
143   Log.debug("Starting default activation checks for #{agent_name}")
144 
145   # Check global state to determine if agent should be loaded
146   should_activate = config.activate_agents
147 
148   # Check agent specific state to determine if agent should be loaded
149   should_activate = Util.str_to_bool(config.pluginconf.fetch("#{agent_name}.activate_agent", 
150                                      should_activate))
151 
152   unless should_activate
153     Log.debug("Found plugin configuration '#{agent_name}.activate_agent' with value '#{should_activate}'")
154   end
155 
156   return should_activate
157 end
new() click to toggle source
   # File lib/mcollective/rpc/agent.rb
37 def initialize
38   @agent_name = self.class.to_s.split("::").last.downcase
39 
40   load_ddl
41 
42   @logger = Log.instance
43   @config = Config.instance
44 
45   # if we have a global authorization provider enable it
46   # plugins can still override it per plugin
47   self.class.authorized_by(@config.rpcauthprovider) if @config.rpcauthorization
48 
49   startup_hook
50 end

Public Instance Methods

handlemsg(msg, connection) click to toggle source
    # File lib/mcollective/rpc/agent.rb
 62 def handlemsg(msg, connection)
 63   @request = RPC::Request.new(msg, @ddl)
 64   @reply = RPC::Reply.new(@request.action, @ddl)
 65 
 66   begin
 67     # Incoming requests need to be validated against the DDL thus reusing
 68     # all the work users put into creating DDLs and creating a consistent
 69     # quality of input validation everywhere with the a simple once off
 70     # investment of writing a DDL
 71     @request.validate!
 72 
 73     # Calls the authorization plugin if any is defined
 74     # if this raises an exception we wil just skip processing this
 75     # message
 76     authorization_hook(@request) if respond_to?("authorization_hook")
 77 
 78     # Audits the request, currently continues processing the message
 79     # we should make this a configurable so that an audit failure means
 80     # a message wont be processed by this node depending on config
 81     audit_request(@request, connection)
 82 
 83     before_processing_hook(msg, connection)
 84 
 85     if respond_to?("#{@request.action}_action")
 86       send("#{@request.action}_action")
 87     else
 88       raise UnknownRPCAction, "Unknown action '#{@request.action}' for agent '#{@request.agent}'"
 89     end
 90   rescue RPCAborted => e
 91     @reply.fail e.to_s, 1
 92 
 93   rescue UnknownRPCAction => e
 94     @reply.fail e.to_s, 2
 95 
 96   rescue MissingRPCData => e
 97     @reply.fail e.to_s, 3
 98 
 99   rescue InvalidRPCData, DDLValidationError => e
100     @reply.fail e.to_s, 4
101 
102   rescue UnknownRPCError => e
103     Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s])
104     Log.error(e.backtrace.join("\n\t"))
105     @reply.fail e.to_s, 5
106 
107   rescue Exception => e
108     Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s])
109     Log.error(e.backtrace.join("\n\t"))
110     @reply.fail e.to_s, 5
111 
112   end
113 
114   after_processing_hook
115 
116   if @request.should_respond?
117     return @reply.to_hash
118   else
119     Log.debug("Client did not request a response, surpressing reply")
120     return nil
121   end
122 end
load_ddl() click to toggle source
   # File lib/mcollective/rpc/agent.rb
52 def load_ddl
53   @ddl = DDL.new(@agent_name, :agent)
54   @meta = @ddl.meta
55   @timeout = @meta[:timeout] || 10
56 
57 rescue Exception => e
58   Log.error("Failed to load DDL for the '%s' agent, DDLs are required: %s: %s" % [@agent_name, e.class, e.to_s])
59   raise DDLValidationError
60 end