X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=doc%2FMCollective%2FRPC%2FAgent.html;fp=doc%2FMCollective%2FRPC%2FAgent.html;h=77d1cd5b71b154004c4314791bfcef75ad2a455a;hb=7c9314f502cde8daad23b61d10b24a542e04154a;hp=0000000000000000000000000000000000000000;hpb=d1f1649ba43c5cbc43c4beb2380096ba051d646a;p=packages%2Fprecise%2Fmcollective.git diff --git a/doc/MCollective/RPC/Agent.html b/doc/MCollective/RPC/Agent.html new file mode 100644 index 0000000..77d1cd5 --- /dev/null +++ b/doc/MCollective/RPC/Agent.html @@ -0,0 +1,1457 @@ + + + +
+ + ++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 +157: def self.actions +158: public_instance_methods.sort.grep(/_action$/).map do |method| +159: $1 if method =~ /(.+)_action$/ +160: end +161: 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 +142: def self.activate? +143: agent_name = self.to_s.split("::").last.downcase +144: +145: log_code(:PLMC37, "Starting default activation checks for the '%{agent}' agent", :debug, :agent => agent_name) +146: +147: should_activate = Util.str_to_bool(Config.instance.pluginconf.fetch("#{agent_name}.activate_agent", true)) +148: +149: unless should_activate +150: log_code(:PLMC38, "Found plugin configuration '%{agent}.activate_agent' with value '%{should_activate}'", :debug, :agent => agent_name, :should_activate => should_activate) +151: end +152: +153: return should_activate +154: end+
(Not documented)
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 40 +40: def initialize +41: @agent_name = self.class.to_s.split("::").last.downcase +42: +43: load_ddl +44: +45: @logger = Log.instance +46: @config = Config.instance +47: +48: # if we have a global authorization provider enable it +49: # plugins can still override it per plugin +50: self.class.authorized_by(@config.rpcauthprovider) if @config.rpcauthorization +51: +52: startup_hook +53: end+
+Creates a new action with the block passed and sets some defaults +
++action “status” do +
++ # logic here to restart service ++
+end +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 260 +260: def self.action(name, &block) +261: raise "Need to pass a body for the action" unless block_given? +262: +263: self.module_eval { define_method("#{name}_action", &block) } +264: end+
+Creates the needed activate? class in a manner similar to the other helpers +like action, authorized_by etc +
++activate_when do +
++ File.exist?("/usr/bin/puppet") ++
+end +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 249 +249: def self.activate_when(&block) +250: (class << self; self; end).instance_eval do +251: define_method("activate?", &block) +252: end +253: end+
+Registers meta data for the introspection hash +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 237 +237: def self.metadata(data) +238: agent = File.basename(caller.first).split(":").first +239: +240: log_code(:PLMC34, "setting meta data in agents have been deprecated, DDL files are now being used for this information. Please update the '%{agent}' agent", :warn, :agent => agent) +241: end+
(Not documented)
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 64 + 64: def handlemsg(msg, connection) + 65: @request = RPC::Request.new(msg, @ddl) + 66: @reply = RPC::Reply.new(@request.action, @ddl) + 67: + 68: begin + 69: # Incoming requests need to be validated against the DDL thus reusing + 70: # all the work users put into creating DDLs and creating a consistent + 71: # quality of input validation everywhere with the a simple once off + 72: # investment of writing a DDL + 73: @request.validate! + 74: + 75: # Calls the authorization plugin if any is defined + 76: # if this raises an exception we wil just skip processing this + 77: # message + 78: authorization_hook(@request) if respond_to?("authorization_hook") + 79: + 80: # Audits the request, currently continues processing the message + 81: # we should make this a configurable so that an audit failure means + 82: # a message wont be processed by this node depending on config + 83: audit_request(@request, connection) + 84: + 85: before_processing_hook(msg, connection) + 86: + 87: if respond_to?("#{@request.action}_action") + 88: send("#{@request.action}_action") + 89: else + 90: log_code(:PLMC36, "Unknown action '%{action}' for agent '%{agent}'", :warn, :action => @request.action, :agent => @request.agent) + 91: raise UnknownRPCAction, "Unknown action '#{@request.action}' for agent '#{@request.agent}'" + 92: end + 93: rescue RPCAborted => e + 94: @reply.fail e.to_s, 1 + 95: + 96: rescue UnknownRPCAction => e + 97: @reply.fail e.to_s, 2 + 98: + 99: rescue MissingRPCData => e +100: @reply.fail e.to_s, 3 +101: +102: rescue InvalidRPCData, DDLValidationError => e +103: @reply.fail e.to_s, 4 +104: +105: rescue UnknownRPCError => e +106: Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s]) +107: Log.error(e.backtrace.join("\n\t")) +108: @reply.fail e.to_s, 5 +109: +110: rescue Exception => e +111: Log.error("%s#%s failed: %s: %s" % [@agent_name, @request.action, e.class, e.to_s]) +112: Log.error(e.backtrace.join("\n\t")) +113: @reply.fail e.to_s, 5 +114: +115: end +116: +117: after_processing_hook +118: +119: if @request.should_respond? +120: return @reply.to_hash +121: else +122: log_code(:PLMC35, "Client did not request a response, surpressing reply", :debug) +123: return nil +124: end +125: end+
(Not documented)
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 55 +55: def load_ddl +56: @ddl = DDL.new(@agent_name, :agent) +57: @meta = @ddl.meta +58: @timeout = @meta[:timeout] || 10 +59: +60: rescue Exception => e +61: 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) +62: end+
+Called at the end of processing just before the response gets sent to the +middleware. +
++This gets run outside of the main exception handling block of the agent so +you should handle any exceptions you could raise yourself. The reason it is +outside of the block is so you’ll have access to even status codes +set by the exception handlers. If you do raise an exception it will just be +passed onto the runner and processing will fail. +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 356 +356: def after_processing_hook +357: end+
+Gets called right after a request was received and calls audit plugins +
++Agents can disable auditing by just overriding +this method with a noop one this might be useful for agents that gets a lot +of requests or simply if you do not care for the auditing in a specific +agent. +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 364 +364: def audit_request(msg, connection) +365: PluginManager["rpcaudit_plugin"].audit_request(msg, connection) if @config.rpcaudit +366: rescue Exception => e +367: logexception(:PLMC39, "Audit failed with an error, processing the request will continue.", :warn, e) +368: end+
+Called just after a message was received from the middleware before it gets +passed to the handlers. @request and @reply will already be set, the msg +passed is the message as received from the normal mcollective runner and +the connection is the actual connector. +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 345 +345: def before_processing_hook(msg, connection) +346: end+
+handles external actions +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 313 +313: def implemented_by(command, type=:json) +314: runner = ActionRunner.new(command, request, type) +315: +316: res = runner.run +317: +318: reply.fail! "Did not receive data from #{command}" unless res.include?(:data) +319: reply.fail! "Reply data from #{command} is not a Hash" unless res[:data].is_a?(Hash) +320: +321: reply.data.merge!(res[:data]) +322: +323: if res[:exitstatus] > 0 +324: reply.fail "Failed to run #{command}: #{res[:stderr]}", res[:exitstatus] +325: end +326: rescue Exception => e +327: Log.warn("Unhandled #{e.class} exception during #{request.agent}##{request.action}: #{e}") +328: reply.fail! "Unexpected failure calling #{command}: #{e.class}: #{e}" +329: end+
+Runs a command via the MC::Shell wrapper, options are as per MC::Shell +
++The simplest use is: +
++ out = "" + err = "" + status = run("echo 1", :stdout => out, :stderr => err) + + reply[:out] = out + reply[:error] = err + reply[:exitstatus] = status ++
+This can be simplified as: +
++ reply[:exitstatus] = run("echo 1", :stdout => :out, :stderr => :error) ++
+You can set a command specific environment and cwd: +
++ run("echo 1", :cwd => "/tmp", :environment => {"FOO" => "BAR"}) ++
+This will run ‘echo 1’ from /tmp with FOO=BAR in addition to a +setting forcing LC_ALL = C. To prevent LC_ALL from being set either set it +specifically or: +
++ run("echo 1", :cwd => "/tmp", :environment => nil) ++
+Exceptions here will be handled by the usual agent exception handler or any +specific one you create, if you dont it will just fall through and be sent +to the client. +
++If the shell handler fails to return a Process::Status instance for exit +status this method will return -1 as the exit status +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 195 +195: def run(command, options={}) +196: shellopts = {} +197: +198: # force stderr and stdout to be strings as the library +199: # will append data to them if given using the << method. +200: # +201: # if the data pased to :stderr or :stdin is a Symbol +202: # add that into the reply hash with that Symbol +203: [:stderr, :stdout].each do |k| +204: if options.include?(k) +205: if options[k].is_a?(Symbol) +206: reply[ options[k] ] = "" +207: shellopts[k] = reply[ options[k] ] +208: else +209: if options[k].respond_to?("<<") +210: shellopts[k] = options[k] +211: else +212: reply.fail! "#{k} should support << while calling run(#{command})" +213: end +214: end +215: end +216: end +217: +218: [:stdin, :cwd, :environment, :timeout].each do |k| +219: if options.include?(k) +220: shellopts[k] = options[k] +221: end +222: end +223: +224: shell = Shell.new(command, shellopts) +225: +226: shell.runcommand +227: +228: if options[:chomp] +229: shellopts[:stdout].chomp! if shellopts[:stdout].is_a?(String) +230: shellopts[:stderr].chomp! if shellopts[:stderr].is_a?(String) +231: end +232: +233: shell.status.exitstatus rescue -1 +234: end+
+convenience wrapper around Util#shellescape +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 308 +308: def shellescape(str) +309: Util.shellescape(str) +310: end+
+Called at the end of the RPC::Agent standard +initialize method use this to adjust meta parameters, timeouts and any +setup you need to do. +
++This will not be called right when the daemon starts up, we use lazy +loading and initialization so it will only be called the first time a +request for this agent arrives. +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 338 +338: def startup_hook +339: end+
+Validates a data member, if validation is a regex then it will try to match +it else it supports testing object types only: +
++validate :msg, String validate :msg, +/^[w\s]+$/ +
++There are also some special helper validators: +
++validate :command, :shellsafe validate :command, :ipv6address validate +:command, :ipv4address validate :command, :boolean validate :command, +[“start”, “stop”] +
++It will raise appropriate exceptions that the RPC +system understand +
+ + + ++ # File lib/mcollective/rpc/agent.rb, line 299 +299: def validate(key, validation) +300: raise MissingRPCData, "please supply a #{key} argument" unless @request.include?(key) +301: +302: Validator.validate(@request[key], validation) +303: rescue ValidatorError => e +304: raise InvalidRPCData, "Input %s did not pass validation: %s" % [ key, e.message ] +305: end+
Disabled; run with --debug to generate this.
+ +Generated with the Darkfish + Rdoc Generator 1.1.6.
+