module MCollective::PluginManager
A simple plugin manager, it stores one plugin each of a specific type the idea is that we can only have one security provider, one connector etc.
Public Class Methods
Adds a plugin to the list of plugins, we expect a hash like:
{:type => "base", :class => foo.new}
or like:
{:type => "base", :class => "Foo::Bar"}
In the event that we already have a class with the given type an exception will be raised.
If the :class passed is a String
then we will delay instantiation till the first time someone asks for the plugin, this is because most likely the registration gets done by inherited() hooks, at which point the plugin class is not final.
If we were to do a .new here the Class initialize method would get called and not the plugins, we there for only initialize the classes when they get requested via []
By default all plugin instances are cached and returned later so there's always a single instance. You can pass :single_instance => false when calling this to instruct it to always return a new instance when a copy is requested. This only works with sending a String
for :class.
# File lib/mcollective/pluginmanager.rb 30 def self.<<(plugin) 31 plugin[:single_instance] = true unless plugin.include?(:single_instance) 32 33 type = plugin[:type] 34 klass = plugin[:class] 35 single = plugin[:single_instance] 36 37 raise("Plugin #{type} already loaded") if @plugins.include?(type) 38 39 40 # If we get a string then store 'nil' as the instance, signalling that we'll 41 # create the class later on demand. 42 if klass.is_a?(String) 43 @plugins[type] = {:loadtime => Time.now, :class => klass, :instance => nil, :single => single} 44 Log.debug("Registering plugin #{type} with class #{klass} single_instance: #{single}") 45 else 46 @plugins[type] = {:loadtime => Time.now, :class => klass.class, :instance => klass, :single => true} 47 Log.debug("Registering plugin #{type} with class #{klass.class} single_instance: true") 48 end 49 end
Gets a plugin by type
# File lib/mcollective/pluginmanager.rb 72 def self.[](plugin) 73 raise("No plugin #{plugin} defined") unless @plugins.include?(plugin) 74 75 klass = @plugins[plugin][:class] 76 77 if @plugins[plugin][:single] 78 # Create an instance of the class if one hasn't been done before 79 if @plugins[plugin][:instance] == nil 80 Log.debug("Returning new plugin #{plugin} with class #{klass}") 81 @plugins[plugin][:instance] = create_instance(klass) 82 else 83 Log.debug("Returning cached plugin #{plugin} with class #{klass}") 84 end 85 86 @plugins[plugin][:instance] 87 else 88 Log.debug("Returning new plugin #{plugin} with class #{klass}") 89 create_instance(klass) 90 end 91 end
deletes all registered plugins
# File lib/mcollective/pluginmanager.rb 67 def self.clear 68 @plugins.clear 69 end
use eval to create an instance of a class
# File lib/mcollective/pluginmanager.rb 94 def self.create_instance(klass) 95 begin 96 eval("#{klass}.new") 97 rescue Exception => e 98 raise("Could not create instance of plugin #{klass}: #{e}") 99 end 100 end
Removes a plugim the list
# File lib/mcollective/pluginmanager.rb 52 def self.delete(plugin) 53 @plugins.delete(plugin) if @plugins.include?(plugin) 54 end
Finds plugins in all configured libdirs
find("agent")
will return an array of just agent names, for example:
["puppetd", "package"]
Can also be used to find files of other extensions:
find("agent", "ddl")
Will return the same list but only of files with extension .ddl in the agent subdirectory
# File lib/mcollective/pluginmanager.rb 116 def self.find(type, extension="rb") 117 extension = ".#{extension}" unless extension.match(/^\./) 118 119 plugins = [] 120 121 Config.instance.libdir.each do |libdir| 122 plugdir = File.join([libdir, "mcollective", type.to_s]) 123 next unless File.directory?(plugdir) 124 125 Dir.new(plugdir).grep(/#{extension}$/).map do |plugin| 126 plugins << File.basename(plugin, extension) 127 end 128 end 129 130 plugins.sort.uniq 131 end
Finds and loads from disk all plugins from all libdirs that match certain criteria.
find_and_load("pluginpackager")
Will find all .rb files in the libdir/mcollective/pluginpackager/ directory in all libdirs and load them from disk.
You can influence what plugins get loaded using a block notation:
find_and_load("pluginpackager") do |plugin| plugin.match(/puppet/) end
This will load only plugins matching /puppet/
# File lib/mcollective/pluginmanager.rb 148 def self.find_and_load(type, extension="rb") 149 extension = ".#{extension}" unless extension.match(/^\./) 150 151 klasses = find(type, extension).map do |plugin| 152 if block_given? 153 next unless yield(plugin) 154 end 155 156 "%s::%s::%s" % [ "MCollective", type.capitalize, plugin.capitalize ] 157 end.compact 158 159 klasses.sort.uniq.each {|klass| loadclass(klass, true)} 160 end
Grep's over the plugin list and returns the list found
# File lib/mcollective/pluginmanager.rb 176 def self.grep(regex) 177 @plugins.keys.grep(regex) 178 end
Finds out if we have a plugin with the given name
# File lib/mcollective/pluginmanager.rb 57 def self.include?(plugin) 58 @plugins.include?(plugin) 59 end
Loads a class from file by doing some simple search/replace on class names and then doing a require.
# File lib/mcollective/pluginmanager.rb 164 def self.loadclass(klass, squash_failures=false) 165 fname = klass.gsub("::", "/").downcase + ".rb" 166 167 Log.debug("Loading #{klass} from #{fname}") 168 169 load fname 170 rescue Exception => e 171 Log.error("Failed to load #{klass}: #{e}") 172 raise unless squash_failures 173 end
Provides a list of plugins we know about
# File lib/mcollective/pluginmanager.rb 62 def self.pluginlist 63 @plugins.keys.sort 64 end