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

# 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