Update mcollective.init according to OSCI-855
[packages/precise/mcollective.git] / website / reference / plugins / application.md
1 ---
2 layout: default
3 title: Single Executable Application Plugin
4 ---
5 [Clients]: ../../simplerpc/clients.html
6 [SimpleRPCAgents]: ../../simplerpc/agents.html
7
8
9 ## Overview
10 The Marionette Collective 1.1.1 and newer supports a single executable - called
11 mco - and have a plugin type called application that lets you create
12 applications for this single executable.
13
14 In the past we tended to write small standalone scripts to interact with
15 MCollective, this had a number of issues:
16
17  * Large number of executables in _/usr/sbin_
18  * Applications behave inconsistently with regard to error handling and reporting
19  * Discovering new applications is difficult since they are all over the filesystem
20  * Installation and packaging of plugins is complex
21
22 We've attempted to address these concerns by creating a single point of access
23 for all applications - the _mco_ script - with unified help, error reporting and
24 option parsing.
25
26 Below you can see the single executable system in use:
27
28 {% highlight console %}
29 The Marionette Collective version 2.0.0
30
31 usage: /usr/bin/mco: command <options>
32
33 Known commands:
34
35    cap                  controller           exim
36    facts                filemgr              find
37    help                 inventory            iptables
38    nettest              nrpe                 package
39    pgrep                ping                 plugin
40    puppetd              rpc                  service
41    virt
42
43 Type 'mco help' for a detailed list of commands and 'mco help command'
44 to get detailed help for a command
45
46 {% endhighlight %}
47
48 {% highlight console %}
49 $ mco help
50 The Marionette Collection version 2.0.0
51
52   facts           Reports on usage for a specific fact
53   filemgr         Generic File Manager Client
54   find            Find hosts matching criteria
55   help            Application list and RPC agent help
56   inventory       Shows an inventory for a given node
57   ping            Ping all nodes
58   rpc             Generic RPC agent client application
59 {% endhighlight %}
60
61 {% highlight console %}
62 $ mco rpc package status package=zsh
63 Determining the amount of hosts matching filter for 2 seconds .... 51
64
65  * [ ============================================================> ] 51 / 51
66
67
68  test.com:
69     Properties:
70        {:provider=>:yum,
71         :release=>"3.el5",
72         :arch=>"x86_64",
73         :version=>"4.2.6",
74         :epoch=>"0",
75         :name=>"zsh",
76         :ensure=>"4.2.6-3.el5"}
77 {% endhighlight %}
78
79 These applications are equivalent to the old mc-rpc and similar applications but without the problem of lots of files in _/usr/sbin_.
80
81 ## Basic Application
82 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].
83
84 {% highlight ruby %}
85 class MCollective::Application::Echo<MCollective::Application
86    description "Reports on usage for a specific fact"
87
88    option :message,
89           :description    => "Message to send",
90           :arguments      => ["-m", "--message MESSAGE"],
91           :type           => String,
92           :required       => true
93
94    def main
95       mc = rpcclient("helloworld")
96
97       printrpc mc.echo(:msg => configuration[:message], :options => options)
98
99       printrpcstats
100    end
101 end
102 {% endhighlight %}
103
104 Here's the application we wrote in action:
105
106 {% highlight console %}
107 $ mco echo
108 The message option is mandatory
109
110 Please run with --help for detailed help
111 {% endhighlight %}
112
113 {% highlight console %}
114 $ mco echo -m test
115
116  * [ ============================================================> ] 1 / 1
117
118
119 example.com
120    Message: test
121       Time: Mon Jan 31 21:27:03 +0000 2011
122
123
124 Finished processing 1 / 1 hosts in 68.53 ms
125 {% endhighlight %}
126
127 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.
128
129 ## Reference
130
131 ### Usage Messages
132
133 To add custom usage messages to your application we can add lines like this:
134
135 {% highlight ruby %}
136 class MCollective::Application::Echo<MCollective::Application
137    description "Reports on usage for a specific fact"
138
139    usage "mco echo [options] --message message"
140 end
141 {% endhighlight %}
142
143 You can add several of these messages by just adding multiple such lines.
144
145 ### Application Options
146
147 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.
148
149 ### CLI Argument Parsing
150
151 There are several options available to assist in parsing CLI arguments. The most basic option is:
152
153 {% highlight ruby %}
154 class MCollective::Application::Echo<MCollective::Application
155    option :message,
156           :description    => "Message to send",
157           :arguments      => ["-m", "--message MESSAGE"]
158 end
159 {% endhighlight %}
160
161 In this case if the user used either _-m message_ or _--message message_ on the CLI the desired message would be in _configuration`[`:message`]`_
162
163 #### Required Arguments
164 You can require that a certain parameter is always passed:
165
166 {% highlight ruby %}
167 option :message,
168   :description    => "Message to send",
169   :arguments      => ["-m", "--message MESSAGE"],
170   :required       => true
171 {% endhighlight %}
172
173 #### Argument data types
174 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.
175
176 You can force data to be of type String, Fixnum etc:
177
178 {% highlight ruby %}
179 option :count,
180   :description    => "Count",
181   :arguments      => ["--count MESSAGE"],
182   :type           => Fixnum
183 {% endhighlight %}
184
185 You can force an argument to be boolean:
186
187 {% highlight ruby %}
188 option :detail,
189   :description    => "Detailed view",
190   :arguments      => ["--detail"],
191   :type           => :bool
192 {% endhighlight %}
193
194 If you have an argument that can be called many times you can force that to build an array:
195
196 {% highlight ruby %}
197 option :args,
198   :description    => "Arguments",
199   :arguments      => ["--argument ARG"],
200   :type           => :array
201 {% endhighlight %}
202
203 Here if you supplied multiple arguments _configuration`[`:args`]`_ will be an array with all the options supplied.
204
205 #### Argument validation
206 You can validate input passed on the CLI:
207
208 {% highlight ruby %}
209 option :count,
210   :description    => "Count",
211   :arguments      => ["--count MESSAGE"],
212   :type           => Fixnum,
213   :validate       => Proc.new {|val| val < 10 ? true : "The message count has to be below 10" }
214 {% endhighlight %}
215
216 Should the supplied value be 10 or more a error message will be displayed.
217
218 #### Disabling standard sections of arguments
219 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.
220
221 {% highlight ruby %}
222 class MCollective::Application::Echo<MCollective::Application
223    exclude_argument_sections "common", "filter", "rpc"
224 end
225 {% endhighlight %}
226
227 This application will only have --help, --verbose and --config as options, all the other options will be removed.
228
229 #### Post argument parsing hook
230 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.
231
232 {% highlight ruby %}
233 class MCollective::Application::Echo<MCollective::Application
234    description "Reports on usage for a specific fact"
235
236    def post_option_parser(configuration)
237       unless ARGV.empty?
238          configuration[:message] = ARGV.shift
239       else
240          STDERR.puts "Please specify a message on the command line"
241          exit 1
242       end
243    end
244
245    def main
246       # use configuration[:message] here to access the message
247    end
248 end
249 {% endhighlight %}
250
251 #### Validating configuration
252 After the options are parsed and the post hook is called you can validate the contents of the configuration:
253
254 {% highlight ruby %}
255 class MCollective::Application::Echo<MCollective::Application
256    description "Reports on usage for a specific fact"
257
258    # parse the first argument as a message
259    def post_option_parser(configuration)
260       configuration[:message] = ARGV.shift unless ARGV.empty?
261    end
262
263    # stop the application if we didnt receive a message
264    def validate_configuration(configuration)
265       raise "Need to supply a message on the command line" unless configuration.include?(:message)
266    end
267
268    def main
269       # use configuration[:message] here to access the message
270    end
271 end
272 {% endhighlight %}
273
274 ### Exiting your application
275 You can use the normal _exit_ Ruby method at any time to exit your application and you can supply any
276 exit code as normal.
277
278 The supplied applications have a standard exit code convention, if you want your applications to exhibit
279 the same behavior use the _halt_ helper.  The exit codes are below:
280
281 |Code|Description                                          |
282 |----|-----------------------------------------------------|
283 |0   |Nodes were discovered and all passed                 |
284 |0   |No discovery was done but responses were received    |
285 |1   |No nodes were discovered                             |
286 |2   |Nodes were discovered but some responses failed      |
287 |3   |Nodes were discovered but no responses were received |
288 |4   |No discovery were done and no responses were received|
289
290 {% highlight ruby %}
291 class MCollective::Application::Echo<MCollective::Application
292    description "Reports on usage for a specific fact"
293
294    def main
295       mc = rpcclient("echo")
296
297       printrpc mc.echo(:msg => "Hello World", :options => options)
298
299       printrpcstats
300
301       halt mc.stats
302    end
303 end
304 {% endhighlight %}
305
306 As you can see you pass the _halt_ helper an instance of the RPC Client statistics and it will then
307 use that to do the right exit code.
308