File: //proc/thread-self/root/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/mcollective/pluginmanager.rb
module MCollective
# 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.
module PluginManager
@plugins = {}
# 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.
def self.<<(plugin)
plugin[:single_instance] = true unless plugin.include?(:single_instance)
type = plugin[:type]
klass = plugin[:class]
single = plugin[:single_instance]
raise("Plugin #{type} already loaded") if @plugins.include?(type)
# If we get a string then store 'nil' as the instance, signalling that we'll
# create the class later on demand.
if klass.is_a?(String)
@plugins[type] = {:loadtime => Time.now, :class => klass, :instance => nil, :single => single}
Log.debug("Registering plugin #{type} with class #{klass} single_instance: #{single}")
else
@plugins[type] = {:loadtime => Time.now, :class => klass.class, :instance => klass, :single => true}
Log.debug("Registering plugin #{type} with class #{klass.class} single_instance: true")
end
end
# Removes a plugim the list
def self.delete(plugin)
@plugins.delete(plugin) if @plugins.include?(plugin)
end
# Finds out if we have a plugin with the given name
def self.include?(plugin)
@plugins.include?(plugin)
end
# Provides a list of plugins we know about
def self.pluginlist
@plugins.keys.sort
end
# deletes all registered plugins
def self.clear
@plugins.clear
end
# Gets a plugin by type
def self.[](plugin)
raise("No plugin #{plugin} defined") unless @plugins.include?(plugin)
klass = @plugins[plugin][:class]
if @plugins[plugin][:single]
# Create an instance of the class if one hasn't been done before
if @plugins[plugin][:instance] == nil
Log.debug("Returning new plugin #{plugin} with class #{klass}")
@plugins[plugin][:instance] = create_instance(klass)
else
Log.debug("Returning cached plugin #{plugin} with class #{klass}")
end
@plugins[plugin][:instance]
else
Log.debug("Returning new plugin #{plugin} with class #{klass}")
create_instance(klass)
end
end
# use eval to create an instance of a class
def self.create_instance(klass)
begin
eval("#{klass}.new")
rescue Exception => e
raise("Could not create instance of plugin #{klass}: #{e}")
end
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
def self.find(type, extension="rb")
extension = ".#{extension}" unless extension.match(/^\./)
plugins = []
Config.instance.libdir.each do |libdir|
plugdir = File.join([libdir, "mcollective", type.to_s])
next unless File.directory?(plugdir)
Dir.new(plugdir).grep(/#{extension}$/).map do |plugin|
plugins << File.basename(plugin, extension)
end
end
plugins.sort.uniq
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/
def self.find_and_load(type, extension="rb")
extension = ".#{extension}" unless extension.match(/^\./)
klasses = find(type, extension).map do |plugin|
if block_given?
next unless yield(plugin)
end
"%s::%s::%s" % [ "MCollective", type.capitalize, plugin.capitalize ]
end.compact
klasses.sort.uniq.each {|klass| loadclass(klass, true)}
end
# Loads a class from file by doing some simple search/replace
# on class names and then doing a require.
def self.loadclass(klass, squash_failures=false)
fname = klass.gsub("::", "/").downcase + ".rb"
Log.debug("Loading #{klass} from #{fname}")
load fname
rescue Exception => e
Log.error("Failed to load #{klass}: #{e}")
raise unless squash_failures
end
# Grep's over the plugin list and returns the list found
def self.grep(regex)
@plugins.keys.grep(regex)
end
end
end