Updated mcollective.init according to OSCI-658
[packages/precise/mcollective.git] / website / reference / plugins / application.md
diff --git a/website/reference/plugins/application.md b/website/reference/plugins/application.md
new file mode 100644 (file)
index 0000000..14e3f8d
--- /dev/null
@@ -0,0 +1,308 @@
+---
+layout: default
+title: Single Executable Application Plugin
+---
+[Clients]: ../../simplerpc/clients.html
+[SimpleRPCAgents]: ../../simplerpc/agents.html
+
+
+## Overview
+The Marionette Collective 1.1.1 and newer supports a single executable - called
+mco - and have a plugin type called application that lets you create
+applications for this single executable.
+
+In the past we tended to write small standalone scripts to interact with
+MCollective, this had a number of issues:
+
+ * Large number of executables in _/usr/sbin_
+ * Applications behave inconsistently with regard to error handling and reporting
+ * Discovering new applications is difficult since they are all over the filesystem
+ * Installation and packaging of plugins is complex
+
+We've attempted to address these concerns by creating a single point of access
+for all applications - the _mco_ script - with unified help, error reporting and
+option parsing.
+
+Below you can see the single executable system in use:
+
+{% highlight console %}
+The Marionette Collective version 2.0.0
+
+usage: /usr/bin/mco: command <options>
+
+Known commands:
+
+   cap                  controller           exim
+   facts                filemgr              find
+   help                 inventory            iptables
+   nettest              nrpe                 package
+   pgrep                ping                 plugin
+   puppetd              rpc                  service
+   virt
+
+Type 'mco help' for a detailed list of commands and 'mco help command'
+to get detailed help for a command
+
+{% endhighlight %}
+
+{% highlight console %}
+$ mco help
+The Marionette Collection version 2.0.0
+
+  facts           Reports on usage for a specific fact
+  filemgr         Generic File Manager Client
+  find            Find hosts matching criteria
+  help            Application list and RPC agent help
+  inventory       Shows an inventory for a given node
+  ping            Ping all nodes
+  rpc             Generic RPC agent client application
+{% endhighlight %}
+
+{% highlight console %}
+$ mco rpc package status package=zsh
+Determining the amount of hosts matching filter for 2 seconds .... 51
+
+ * [ ============================================================> ] 51 / 51
+
+
+ test.com:
+    Properties:
+       {:provider=>:yum,
+       :release=>"3.el5",
+       :arch=>"x86_64",
+       :version=>"4.2.6",
+       :epoch=>"0",
+       :name=>"zsh",
+       :ensure=>"4.2.6-3.el5"}
+{% endhighlight %}
+
+These applications are equivalent to the old mc-rpc and similar applications but without the problem of lots of files in _/usr/sbin_.
+
+## Basic Application
+Applications goes in _libdir/mcollective/application/echo.rb_, the one below is a simple application that speaks to a hypothetical _echo_ action of a _helloworld_ agent. This agent has been demonstrated in : writing [agents][SimpleRPCAgents].
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   description "Reports on usage for a specific fact"
+
+   option :message,
+          :description    => "Message to send",
+          :arguments      => ["-m", "--message MESSAGE"],
+          :type           => String,
+          :required       => true
+
+   def main
+      mc = rpcclient("helloworld")
+
+      printrpc mc.echo(:msg => configuration[:message], :options => options)
+
+      printrpcstats
+   end
+end
+{% endhighlight %}
+
+Here's the application we wrote in action:
+
+{% highlight console %}
+$ mco echo
+The message option is mandatory
+
+Please run with --help for detailed help
+{% endhighlight %}
+
+{% highlight console %}
+$ mco echo -m test
+
+ * [ ============================================================> ] 1 / 1
+
+
+example.com
+   Message: test
+      Time: Mon Jan 31 21:27:03 +0000 2011
+
+
+Finished processing 1 / 1 hosts in 68.53 ms
+{% endhighlight %}
+
+Most of the techniques documented in SimpleRPC [Clients] can be reused here, we've just simplified a lot of the common used patterns like CLI arguments and incorporated it all in a single framework.
+
+## Reference
+
+### Usage Messages
+
+To add custom usage messages to your application we can add lines like this:
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   description "Reports on usage for a specific fact"
+
+   usage "mco echo [options] --message message"
+end
+{% endhighlight %}
+
+You can add several of these messages by just adding multiple such lines.
+
+### Application Options
+
+A standard options hash is available simply as options you can manipulate it and pass it into the RPC Client like normal. See the SimpleRPC [Clients] reference for more on this.
+
+### CLI Argument Parsing
+
+There are several options available to assist in parsing CLI arguments. The most basic option is:
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   option :message,
+          :description    => "Message to send",
+          :arguments      => ["-m", "--message MESSAGE"]
+end
+{% endhighlight %}
+
+In this case if the user used either _-m message_ or _--message message_ on the CLI the desired message would be in _configuration`[`:message`]`_
+
+#### Required Arguments
+You can require that a certain parameter is always passed:
+
+{% highlight ruby %}
+option :message,
+  :description    => "Message to send",
+  :arguments      => ["-m", "--message MESSAGE"],
+  :required       => true
+{% endhighlight %}
+
+#### Argument data types
+CLI arguments can be forced to a specific type, we also have some additional special types that the default ruby option parser cant handle on its own.
+
+You can force data to be of type String, Fixnum etc:
+
+{% highlight ruby %}
+option :count,
+  :description    => "Count",
+  :arguments      => ["--count MESSAGE"],
+  :type           => Fixnum
+{% endhighlight %}
+
+You can force an argument to be boolean:
+
+{% highlight ruby %}
+option :detail,
+  :description    => "Detailed view",
+  :arguments      => ["--detail"],
+  :type           => :bool
+{% endhighlight %}
+
+If you have an argument that can be called many times you can force that to build an array:
+
+{% highlight ruby %}
+option :args,
+  :description    => "Arguments",
+  :arguments      => ["--argument ARG"],
+  :type           => :array
+{% endhighlight %}
+
+Here if you supplied multiple arguments _configuration`[`:args`]`_ will be an array with all the options supplied.
+
+#### Argument validation
+You can validate input passed on the CLI:
+
+{% highlight ruby %}
+option :count,
+  :description    => "Count",
+  :arguments      => ["--count MESSAGE"],
+  :type           => Fixnum,
+  :validate       => Proc.new {|val| val < 10 ? true : "The message count has to be below 10" }
+{% endhighlight %}
+
+Should the supplied value be 10 or more a error message will be displayed.
+
+#### Disabling standard sections of arguments
+By default every Application get all the RPC options enabling filtering, discovery etc.  In some cases this is undesirable so we let users disable those.
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   exclude_argument_sections "common", "filter", "rpc"
+end
+{% endhighlight %}
+
+This application will only have --help, --verbose and --config as options, all the other options will be removed.
+
+#### Post argument parsing hook
+Right after all arguments are parsed you can have a hook in your program called, this hook could perhaps parse the remaining data on _ARGV_ after option parsing is complete.
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   description "Reports on usage for a specific fact"
+
+   def post_option_parser(configuration)
+      unless ARGV.empty?
+         configuration[:message] = ARGV.shift
+      else
+         STDERR.puts "Please specify a message on the command line"
+         exit 1
+      end
+   end
+
+   def main
+      # use configuration[:message] here to access the message
+   end
+end
+{% endhighlight %}
+
+#### Validating configuration
+After the options are parsed and the post hook is called you can validate the contents of the configuration:
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   description "Reports on usage for a specific fact"
+
+   # parse the first argument as a message
+   def post_option_parser(configuration)
+      configuration[:message] = ARGV.shift unless ARGV.empty?
+   end
+
+   # stop the application if we didnt receive a message
+   def validate_configuration(configuration)
+      raise "Need to supply a message on the command line" unless configuration.include?(:message)
+   end
+
+   def main
+      # use configuration[:message] here to access the message
+   end
+end
+{% endhighlight %}
+
+### Exiting your application
+You can use the normal _exit_ Ruby method at any time to exit your application and you can supply any
+exit code as normal.
+
+The supplied applications have a standard exit code convention, if you want your applications to exhibit
+the same behavior use the _halt_ helper.  The exit codes are below:
+
+|Code|Description                                          |
+|----|-----------------------------------------------------|
+|0   |Nodes were discovered and all passed                 |
+|0   |No discovery was done but responses were received    |
+|1   |No nodes were discovered                             |
+|2   |Nodes were discovered but some responses failed      |
+|3   |Nodes were discovered but no responses were received |
+|4   |No discovery were done and no responses were received|
+
+{% highlight ruby %}
+class MCollective::Application::Echo<MCollective::Application
+   description "Reports on usage for a specific fact"
+
+   def main
+      mc = rpcclient("echo")
+
+      printrpc mc.echo(:msg => "Hello World", :options => options)
+
+      printrpcstats
+
+      halt mc.stats
+   end
+end
+{% endhighlight %}
+
+As you can see you pass the _halt_ helper an instance of the RPC Client statistics and it will then
+use that to do the right exit code.
+