Basic Agents and Clients
In general you should use SimpleRPC 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.
SimpleRPC though is a wrapper around the core message flow and routing components, this page documents the core. You should first read up about SimpleRPC before delving into this.
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.
The agent will send back everything that got sent to it, not overly useful but enough to demonstrate the concepts.
The Agent
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.
Create a file echo.rb with the following, I’ll walk through each part:
1 module MCollective
2 module Agent
3 # Simple echo agent
4 class Echo
5 attr_reader :timeout, :meta
6
7 def initialize
8 @timeout = 5
9 @meta = {:license => "Apache License, Version 2",
10 :author => "R.I.Pienaar <rip@devco.net>",
11 :version => "1.0"}
12 end
13
14 def handlemsg(msg, stomp)
15 msg[:body]
16 end
17
18 def help
19 <<-EOH
20 Echo Agent
21 ==========
22
23 Simple agent that just sends the body of any request back
24 EOH
25 end
26 end
27 end
28 end
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:
% /usr/sbin/mc-call-agent --config etc/client.cfg --agent echo --arg hello
Determining the amount of hosts matching filter for 2 seconds .... 1
devel.your.com>
"hello"
---- stomp call summary ----
Nodes: 1 / 1
Start Time: Tue Nov 03 23:18:40 +0000 2009
Discovery Time: 2001.65ms
Agent Time: 44.17ms
Total Time: 2045.82ms
Agent name
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.
module MCollective
module Agent
class Echo
end
end
end
Initialization
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.
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.
attr_reader :timeout, :meta
def initialize
@timeout = 1
@meta = {:license => "Apache License, Version 2",
:author => "R.I.Pienaar <rip@devco.net>",
:version => "1.0"}
end
Handling incoming messages
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.
Whenever a message for your agent pass all the checks it will be passed to you via the handlemsg method.
def handlemsg(msg, stomp)
msg[:body]
end
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.
Online Help
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.
def help
<<-EOH
Echo Agent
==========
Simple agent that just sends the body of any request back
EOH
end
More complex agents
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).
Create the util folder if needed.
The classes should be MCollective::Util::Yourclass and you should use the following construct to require them from disk:
MCollective::Util.loadclass("MCollective::Util::Yourclass")
Create util/yourclass.rb with this content :
module MCollective
module Util
class Yourclass
# The class definition here
end
end
end
loadclass on Yourclass will automatically search for a yourclass.rb file (lowercase).
At present simply requiring them will work and we’ll hope to maintain that but to be 100% future safe use this method.
It also loads modules in exactly the same way.
The Client
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.
1 #!/usr/bin/ruby
2
3 require 'mcollective'
4
5 oparser = MCollective::Optionparser.new({}, "filter")
6
7 options = oparser.parse{|parser, options|
8 parser.define_head "Tester for the echo agent"
9 parser.banner = "Usage: mc-echo [options] msg"
10 }
11
12 if ARGV.length > 0
13 msg = ARGV.shift
14 else
15 puts("Please specify a message to send")
16 exit 1
17 end
18
19 begin
20 options[:filter]["agent"] = "echo"
21
22 client = MCollective::Client.new(options[:config])
23
24 stats = client.discovered_req(msg, "echo", options) do |resp|
25 next if resp == nil
26
27 printf("%30s> %s\n", resp[:senderid], resp[:body])
28 end
29 rescue Exception => e
30 puts("Failed to contact any agents: #{e}")
31 exit 1
32 end
33
34 client.display_stats(stats, options)
We can test it works as below:
% ./mc-echo --config etc/client.cfg "hello world"
Determining the amount of hosts matching filter for 2 seconds .... 1
devel.your.com> hello world
Finished processing 1 / 1 hosts in 45.02 ms
Verbose statistics:
% ./mc-echo --config etc/client.cfg "hello world" -v
Determining the amount of hosts matching filter for 2 seconds .... 1
devel.your.com> hello world
---- stomp call summary ----
Nodes: 1 / 1
Start Time: Tue Nov 03 23:28:34 +0000 2009
Discovery Time: 2002.27ms
Agent Time: 45.84ms
Total Time: 2048.11ms
Discovery and filtering:
% ./mc-echo --config etc/client.cfg "hello world" --with-fact country=au
Failed to contact any agents: No matching clients found
% ./mc-echo --config etc/client.cfg "hello world" --with-fact country=uk
Determining the amount of hosts matching filter for 2 seconds .... 1
devel.your.com> hello world
Finished processing 1 / 1 hosts in 38.77 ms
Standard Help:
% ./mc-echo --help
Usage: mc-echo [options] msg
Tester for the echo agent
Common Options
-c, --config FILE Load configuratuion from file rather than default
--dt SECONDS Timeout for doing discovery
--discovery-timeout
-t, --timeout SECONDS Timeout for calling remote agents
-q, --quiet Do not be verbose
-v, --verbose Be verbose
-h, --help Display this screen
Host Filters
--wf, --with-fact fact=val Match hosts with a certain fact
--wc, --with-class CLASS Match hosts with a certain configuration management class
--wa, --with-agent AGENT Match hosts with a certain agent
--wi, --with-identity IDENT Match hosts with a certain configured identity