Update mcollective.init according to OSCI-855
[packages/precise/mcollective.git] / website / reference / basic / basic_agent_and_client.md
1 ---
2 layout: default
3 title: Basic Agents and Clients
4 ---
5 [SimpleRPCIntroduction]: /mcollective/simplerpc/
6
7 ## Deprecation Warning
8
9 You must use [SimpleRPC][SimpleRPCIntroduction] for writing agents and clients.  SimpleRPC is a framework that wraps the core MCollective client and agent structures in a lot of helpful convention and tools.
10
11 **The approach to developing agents and clients documented below is not supported post version 2.0.0 and the functionality will be removed for the next major release.**
12
13 ## Overview
14
15 Writing agents for mcollective is simple, we'll write a simple _echo_ agent as well as a cli tool to communicate with it that supports discovery, filtering and more.
16
17 The agent will send back everything that got sent to it, not overly useful but enough to demonstrate the concepts.
18
19 ## The Agent
20
21 Agents go into a directory configured in the _server.cfg_ using the _libdir_ directive.  You should have _mcollective/agent_ directory under that, restart the daemon when you've put it there.
22
23 Create a file echo.rb with the following, I'll walk through each part:
24
25 {% highlight ruby linenos %}
26 module MCollective
27     module Agent
28         # Simple echo agent
29         class Echo
30             attr_reader :timeout, :meta
31
32             def initialize
33                 @timeout = 5
34                 @meta = {:license => "Apache License, Version 2",
35                          :author => "R.I.Pienaar <rip@devco.net>",
36                          :version => "1.0"}
37             end
38
39             def handlemsg(msg, stomp)
40                 msg[:body]
41             end
42
43             def help
44             <<-EOH
45             Echo Agent
46             ==========
47
48             Simple agent that just sends the body of any request back
49             EOH
50             end
51         end
52     end
53 end
54 {% endhighlight %}
55
56 Once you have it running you can test your agent works as below, we'll send the word _hello_ to the agent and we'll see if we get it back:
57
58 {% highlight console %}
59 % /usr/sbin/mc-call-agent --config etc/client.cfg --agent echo --arg hello
60 Determining the amount of hosts matching filter for 2 seconds .... 1
61
62 devel.your.com>
63 "hello"
64
65 ---- stomp call summary ----
66            Nodes: 1 / 1
67       Start Time: Tue Nov 03 23:18:40 +0000 2009
68   Discovery Time: 2001.65ms
69       Agent Time: 44.17ms
70       Total Time: 2045.82ms
71 {% endhighlight %}
72
73
74 ### Agent name
75 Each agent should be wrapped in a module and class as below, this will create an agent called _echo_ and should live in a file called _agents/echo.rb_.
76
77 {% highlight ruby %}
78 module MCollective
79     module Agent
80         class Echo
81         end
82     end
83 end
84 {% endhighlight %}
85
86 ### Initialization
87 Every agent needs to specify a timeout and meta data.  The timeout gets used by the app server to kill off agents that is taking too long to finish.
88
89 Meta data contains some information about the licence, author and version of the agent.  Right now the information is free-form but I suggest supplying at the very least the details below as we'll start enforcing the existence of it in future.
90
91 {% highlight ruby %}
92             attr_reader :timeout, :meta
93
94             def initialize
95                 @timeout = 1
96                 @meta = {:license => "Apache License, Version 2",
97                          :author => "R.I.Pienaar <rip@devco.net>",
98                          :version => "1.0"}
99             end
100 {% endhighlight %}
101
102 ### Handling incoming messages
103 You do not need to be concerned with filtering, authentication, authorization etc when writing agents - the app server takes care of all of that for you.
104
105 Whenever a message for your agent pass all the checks it will be passed to you via the _handlemsg_ method.
106
107 {% highlight ruby %}
108             def handlemsg(msg, stomp)
109                 msg[:body]
110             end
111 {% endhighlight %}
112
113 The msg will be a hash with several keys giving you information about sender, hashes, time it was sent and so forth, in our case we just want to know about the body and we're sending it right back as a return value.
114
115 ### Online Help
116 We keep help information, not used right now but future version of the code will have a simple way to get help for each agent, the intent is that inputs, outputs and behavior to all agents would be described in this.
117
118 {% highlight ruby %}
119             def help
120             <<-EOH
121             Echo Agent
122             ==========
123
124             Simple agent that just sends the body of any request back
125             EOH
126             end
127 {% endhighlight %}
128
129 ## More complex agents
130 As you write more complex agents and clients you might find the need to have a few separate files make up your agent, you can drop these files into a directory named _util_ under the plugins (that is, at the same level of the agent folder, so as _/usr/libexec/mcollective/mcollective/util_ at the time of writing).
131
132 Create the _util_ folder if needed.
133
134 The classes should be _MCollective::Util::Yourclass_ and you should use the following construct to require them from disk:
135
136 {% highlight ruby %}
137 MCollective::Util.loadclass("MCollective::Util::Yourclass")
138 {% endhighlight %}
139
140 Create _util/yourclass.rb_ with this content :
141
142 {% highlight ruby %}
143 module MCollective
144  module Util
145    class Yourclass
146      # The class definition here
147    end
148  end
149 end
150 {% endhighlight %}
151
152 _loadclass_ on _Yourclass_ will automatically search for a _yourclass.rb_ file (lowercase).
153
154 At present simply requiring them will work and we'll hope to maintain that but to be 100% future safe use this method.
155
156
157 It also loads modules in exactly the same way.
158
159 ## The Client
160 We provide a client library that abstracts away a lot of the work in writing simple attractive cli frontends to your agents that supports discovery, filtering, generates help etc.  The client lib is functional but we will improve/refactor the options parsing a bit in future.
161
162 {% highlight ruby linenos %}
163 #!/usr/bin/ruby
164
165 require 'mcollective'
166
167 oparser = MCollective::Optionparser.new({}, "filter")
168
169 options = oparser.parse{|parser, options|
170     parser.define_head "Tester for the echo agent"
171     parser.banner = "Usage: mc-echo [options] msg"
172 }
173
174 if ARGV.length > 0
175     msg = ARGV.shift
176 else
177     puts("Please specify a message to send")
178     exit 1
179 end
180
181 begin
182     options[:filter]["agent"] = "echo"
183
184     client = MCollective::Client.new(options[:config])
185
186     stats = client.discovered_req(msg, "echo", options) do |resp|
187         next if resp == nil
188
189         printf("%30s> %s\n", resp[:senderid], resp[:body])
190     end
191 rescue Exception => e
192     puts("Failed to contact any agents: #{e}")
193     exit 1
194 end
195
196 client.display_stats(stats, options)
197 {% endhighlight %}
198
199 We can test it works as below:
200
201 {% highlight console %}
202 % ./mc-echo --config etc/client.cfg "hello world"
203 Determining the amount of hosts matching filter for 2 seconds .... 1
204
205                 devel.your.com> hello world
206
207 Finished processing 1 / 1 hosts in 45.02 ms
208 {% endhighlight %}
209
210 Verbose statistics:
211
212 {% highlight console %}
213 % ./mc-echo --config etc/client.cfg "hello world" -v
214 Determining the amount of hosts matching filter for 2 seconds .... 1
215
216                 devel.your.com> hello world
217
218 ---- stomp call summary ----
219            Nodes: 1 / 1
220       Start Time: Tue Nov 03 23:28:34 +0000 2009
221   Discovery Time: 2002.27ms
222       Agent Time: 45.84ms
223       Total Time: 2048.11ms
224 {% endhighlight %}
225
226 Discovery and filtering:
227
228 {% highlight console %}
229 % ./mc-echo --config etc/client.cfg "hello world" --with-fact country=au
230
231 Failed to contact any agents: No matching clients found
232
233 % ./mc-echo --config etc/client.cfg "hello world" --with-fact country=uk
234 Determining the amount of hosts matching filter for 2 seconds .... 1
235
236                 devel.your.com> hello world
237
238 Finished processing 1 / 1 hosts in 38.77 ms
239 {% endhighlight %}
240
241 Standard Help:
242
243 {% highlight console %}
244 % ./mc-echo --help
245 Usage: mc-echo [options] msg
246 Tester for the echo agent
247
248 Common Options
249     -c, --config FILE                Load configuratuion from file rather than default
250         --dt SECONDS                 Timeout for doing discovery
251         --discovery-timeout
252     -t, --timeout SECONDS            Timeout for calling remote agents
253     -q, --quiet                      Do not be verbose
254     -v, --verbose                    Be verbose
255     -h, --help                       Display this screen
256
257 Host Filters
258         --wf, --with-fact fact=val   Match hosts with a certain fact
259         --wc, --with-class CLASS     Match hosts with a certain configuration management class
260         --wa, --with-agent AGENT     Match hosts with a certain agent
261         --wi, --with-identity IDENT  Match hosts with a certain configured identity
262 {% endhighlight %}