Make the registration target collective configurable. Refactor the base class to be more testable and provide test coverage for non threaded code. Update documentation.
Signed-off-by: R.I.Pienaar <[email protected]> --- Local-branch: feature/master/7650 lib/mcollective/config.rb | 6 ++- lib/mcollective/registration/base.rb | 74 +++++++++++++++++------ spec/unit/registration/base_spec.rb | 93 +++++++++++++++++++++++++++++ website/changelog.md | 1 + website/reference/basic/configuration.md | 1 + website/reference/plugins/registration.md | 20 ++++--- 6 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 spec/unit/registration/base_spec.rb diff --git a/lib/mcollective/config.rb b/lib/mcollective/config.rb index 8574472..6c8e8e4 100644 --- a/lib/mcollective/config.rb +++ b/lib/mcollective/config.rb @@ -8,7 +8,8 @@ module MCollective :securityprovider, :factsource, :registration, :registerinterval, :topicsep, :classesfile, :rpcauditprovider, :rpcaudit, :configdir, :rpcauthprovider, :rpcauthorization, :color, :configfile, :rpchelptemplate, :rpclimitmethod, - :logger_type, :fact_cache_time, :collectives, :main_collective, :ssl_cipher + :logger_type, :fact_cache_time, :collectives, :main_collective, :ssl_cipher, + :registration_collective def initialize @configured = false @@ -33,6 +34,8 @@ module MCollective @topicsep = val when "registration" @registration = val.capitalize + when "registration_collective" + @registration_collective = val when "registerinterval" @registerinterval = val.to_i when "collectives" @@ -125,6 +128,7 @@ module MCollective @identity = Socket.gethostname @registration = "Agentlist" @registerinterval = 0 + @registration_collective = nil @topicsep = "." @classesfile = "/var/lib/puppet/classes.txt" @rpcaudit = false diff --git a/lib/mcollective/registration/base.rb b/lib/mcollective/registration/base.rb index 969ee30..9859f61 100644 --- a/lib/mcollective/registration/base.rb +++ b/lib/mcollective/registration/base.rb @@ -17,35 +17,73 @@ module MCollective # The actual registration notices comes from the 'body' method of the registration # plugins. def run(connection) - config = Config.instance - return if config.registerinterval == 0 + return false if interval == 0 Thread.new do loop do begin - target = Util.make_target("registration", :command, config.main_collective) - reqid = Digest::MD5.hexdigest("#{config.identity}-#{Time.now.to_f.to_s}-#{target}") - filter = {"agent" => "registration"} + publish(body, connection) - registration_message = body - - unless registration_message.nil? - req = PluginManager["security_plugin"].encoderequest(config.identity, target, registration_message, reqid, filter) - - Log.debug("Sending registration #{reqid} to #{target}") - connection.publish(target, req) - else - Log.debug("Skipping registration due to nil body") - end - - sleep config.registerinterval + sleep interval rescue Exception => e Log.error("Sending registration message failed: #{e}") - sleep config.registerinterval + sleep interval end end end end + + def config + Config.instance + end + + def identity + config.identity + end + + def msg_filter + {"agent" => "registration"} + end + + def msg_id(target) + reqid = Digest::MD5.hexdigest("#{config.identity}-#{Time.now.to_f.to_s}-#{target}") + end + + def msg_target + Util.make_target("registration", :command, target_collective) + end + + def target_collective + main_collective = config.main_collective + + collective = config.registration_collective || main_collective + + unless config.collectives.include?(collective) + Log.warn("Sending registration to #{main_collective}: #{collective} is not a valid collective") + collective = main_collective + end + + return collective + end + + def interval + config.registerinterval + end + + def publish(message, connection) + unless message + Log.debug("Skipping registration due to nil body") + else + target = msg_target + reqid = msg_id(target) + + req = PluginManager["security_plugin"].encoderequest(identity, target, message, reqid, msg_filter) + + Log.debug("Sending registration #{reqid} to #{target}") + + connection.publish(target, req) + end + end end end end diff --git a/spec/unit/registration/base_spec.rb b/spec/unit/registration/base_spec.rb new file mode 100644 index 0000000..c59b373 --- /dev/null +++ b/spec/unit/registration/base_spec.rb @@ -0,0 +1,93 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +module MCollective + module Registration + describe Base do + before do + @config = mock + @config.stubs(:identity).returns("rspec") + @config.stubs(:main_collective).returns("main_collective") + Config.stubs(:instance).returns(@config) + + @reg = Base.new + end + + describe "#config" do + it "should provide access the main configuration class" do + @reg.config.should == @config + end + + end + + describe "#identity" do + it "should return the correct identity" do + @reg.config.identity.should == "rspec" + end + end + + describe "#msg_filter" do + it "should target the registration agent" do + @reg.msg_filter.should == {"agent" => "registration"} + end + end + + describe "#msg_id" do + it "should create the message id correctly" do + Digest::MD5.expects(:hexdigest).with(regexp_matches(/rspec-.+-test/)) + @reg.msg_id("test") + end + end + + describe "#msg_target" do + it "should create a target for the correct agent and collective" do + @reg.expects(:target_collective).returns("test").once + Util.expects(:make_target).with("registration", :command, "test").once + @reg.msg_target + end + end + + describe "#target_collective" do + it "should return the configured registration_collective" do + @config.expects(:registration_collective).returns("registration").once + @config.expects(:collectives).returns(["main_collective", "registration"]).once + @reg.target_collective.should == "registration" + end + + it "should use the main collective if registration collective is not valid" do + @config.expects(:registration_collective).returns("registration").once + @config.expects(:collectives).returns(["main_collective"]).once + + Log.expects(:warn).with("Sending registration to main_collective: registration is not a valid collective").once + + @reg.target_collective.should == "main_collective" + end + end + + describe "#publish" do + it "should skip registration for empty messages" do + Log.expects(:debug).with("Skipping registration due to nil body") + @reg.publish(nil, nil) + end + + it "should encode the request via the security plugin and publish correctly" do + security_plugin = mock + connection = mock + + PluginManager.expects("[]").with("security_plugin").returns(security_plugin) + + @reg.expects(:msg_target).returns("target").once + @reg.expects(:msg_id).with("target").returns("msgid").once + @reg.expects(:msg_filter).returns("msgfilter").once + + security_plugin.expects(:encoderequest).with("rspec", "target", "message", "msgid", "msgfilter").returns("req").once + connection.expects(:publish).with("target", "req") + Log.expects(:debug).with("Sending registration msgid to target") + + @reg.publish("message", connection) + end + end + end + end +end diff --git a/website/changelog.md b/website/changelog.md index ee739b1..8a4ff1b 100644 --- a/website/changelog.md +++ b/website/changelog.md @@ -11,6 +11,7 @@ title: Changelog |Date|Description|Ticket| |----|-----------|------| +|2011/05/25|Make the target collective for registration messages configurable|7650| |2011/05/24|Rename the connector plugins send method to publish to avoid issues ruby Object#send|7623| |2011/05/23|Log a warning when the CF file parsing fails rather than raise a whole ruby exception|7627| |2011/05/23|Allow applications to use the exit method as would normally be expected|7626| diff --git a/website/reference/basic/configuration.md b/website/reference/basic/configuration.md index 37f7307..9b70448 100644 --- a/website/reference/basic/configuration.md +++ b/website/reference/basic/configuration.md @@ -53,6 +53,7 @@ The server configuration file should be root only readable |factsource|Facter|Which fact plugin to use| |registration|Agentlist|[Registration] plugin to use| |registerinterval|120|How many seconds to sleep between registration messages, setting this to zero disables registration| +|registration_collective|development|Which sub-collective to send registration messages to| |classesfile|/var/lib/puppet/classes.txt|Where to find a list of classes configured by your configuration management system| |rpcaudit|1|Enables [SimpleRPC Auditing][Auditing]| |rpcauditprovider|Logfile|Enables auditing using _MCollective::Audit::Logfile_| diff --git a/website/reference/plugins/registration.md b/website/reference/plugins/registration.md index 7ff637f..9392f23 100644 --- a/website/reference/plugins/registration.md +++ b/website/reference/plugins/registration.md @@ -8,7 +8,7 @@ disqus: true # {{page.title}} MCollective supports the ability for each node to register with a central inventory. The core functionality -of Mcollective doesn't require registration internally; it's simply provided as a framework to enable you to +of Mcollective doesn't require registration internally; it's simply provided as a framework to enable you to build inventory systems or Web UIs. ## Details @@ -31,28 +31,30 @@ module MCollective end {% endhighlight %} -You can see it's very simple, you just need to subclass *MCollective::Registration::Base* to ensure they get +You can see it's very simple, you just need to subclass *MCollective::Registration::Base* to ensure they get loaded into the plugin system and provide a _body_ method whose return value will be sent to the registration agent(s). As of version 1.1.2 the registration plugin can decide if a message should be sent or not. If your plugin responds with a _nil_ value the message will not be sent. This can be useful if you wish to only send registration data when some condition has changed. On large collectives, registration messages can be -quite high-volume. It's worthwhile to sample the size of your registration messages and multiply by the number -of nodes to determine an appropriate frequency to send them. +quite high-volume. It's worthwhile to sample the size of your registration messages and multiply by the number +of nodes to determine an appropriate frequency to send them. To configure it to be used you just need the following in your config: {% highlight ini %} registerinterval = 300 registration = Agentlist +registration_collective = development {% endhighlight %} -This will cause the plugin to be called every 300 seconds. +This will cause the plugin to be called every 300 seconds to the development collective, if you do not configure +a target collective explicitely it will target the main collective for the given node. We do not provide the receiving end of this in the core mcollective. You will need to write an agent called *registration* and do something useful with the data you receive from all the nodes. You can find -[a simple monitoring system][RegistrationMonitor] built using this method as an example. The receiving agent -can simply be installed as an extra mcollectived plugin on a node which participates in the collective. +[a simple monitoring system][RegistrationMonitor] built using this method as an example. The receiving agent +can simply be installed as an extra mcollectived plugin on a node which participates in the collective. You need to note a few things about the receiving agents: @@ -62,6 +64,6 @@ You need to note a few things about the receiving agents: an indication that you do not want to send back any reply. Replying to registration requests is almost always undesired. There's nothing preventing you from running more than one type of receiving agent in your collective, you can have one -on your monitor server as above and another with completely different code on a web server feeding a local -cache for your web interfaces. As long as both agents are called *registration* you'll be fine. However this +on your monitor server as above and another with completely different code on a web server feeding a local +cache for your web interfaces. As long as both agents are called *registration* you'll be fine. However this does mean you can't run more than one registration receiver on the same server. -- 1.7.1 -- You received this message because you are subscribed to the Google Groups "Puppet Developers" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
