2 # A simple plugin manager, it stores one plugin each of a specific type
3 # the idea is that we can only have one security provider, one connector etc.
7 # Adds a plugin to the list of plugins, we expect a hash like:
14 # :class => "Foo::Bar"}
16 # In the event that we already have a class with the given type
17 # an exception will be raised.
19 # If the :class passed is a String then we will delay instantiation
20 # till the first time someone asks for the plugin, this is because most likely
21 # the registration gets done by inherited() hooks, at which point the plugin class is not final.
23 # If we were to do a .new here the Class initialize method would get called and not
24 # the plugins, we there for only initialize the classes when they get requested via []
26 # By default all plugin instances are cached and returned later so there's
27 # always a single instance. You can pass :single_instance => false when
28 # calling this to instruct it to always return a new instance when a copy
29 # is requested. This only works with sending a String for :class.
31 plugin[:single_instance] = true unless plugin.include?(:single_instance)
34 klass = plugin[:class]
35 single = plugin[:single_instance]
37 raise("Plugin #{type} already loaded") if @plugins.include?(type)
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}")
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")
51 # Removes a plugim the list
52 def self.delete(plugin)
53 @plugins.delete(plugin) if @plugins.include?(plugin)
56 # Finds out if we have a plugin with the given name
57 def self.include?(plugin)
58 @plugins.include?(plugin)
61 # Provides a list of plugins we know about
66 # deletes all registered plugins
71 # Gets a plugin by type
73 raise("No plugin #{plugin} defined") unless @plugins.include?(plugin)
75 klass = @plugins[plugin][:class]
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)
83 Log.debug("Returning cached plugin #{plugin} with class #{klass}")
86 @plugins[plugin][:instance]
88 Log.debug("Returning new plugin #{plugin} with class #{klass}")
89 create_instance(klass)
93 # use eval to create an instance of a class
94 def self.create_instance(klass)
98 raise("Could not create instance of plugin #{klass}: #{e}")
102 # Finds plugins in all configured libdirs
106 # will return an array of just agent names, for example:
108 # ["puppetd", "package"]
110 # Can also be used to find files of other extensions:
112 # find("agent", "ddl")
114 # Will return the same list but only of files with extension .ddl
115 # in the agent subdirectory
116 def self.find(type, extension="rb")
117 extension = ".#{extension}" unless extension.match(/^\./)
121 Config.instance.libdir.each do |libdir|
122 plugdir = File.join([libdir, "mcollective", type.to_s])
123 next unless File.directory?(plugdir)
125 Dir.new(plugdir).grep(/#{extension}$/).map do |plugin|
126 plugins << File.basename(plugin, extension)
133 # Finds and loads from disk all plugins from all libdirs that match
136 # find_and_load("pluginpackager")
138 # Will find all .rb files in the libdir/mcollective/pluginpackager/
139 # directory in all libdirs and load them from disk.
141 # You can influence what plugins get loaded using a block notation:
143 # find_and_load("pluginpackager") do |plugin|
144 # plugin.match(/puppet/)
147 # This will load only plugins matching /puppet/
148 def self.find_and_load(type, extension="rb")
149 extension = ".#{extension}" unless extension.match(/^\./)
151 klasses = find(type, extension).map do |plugin|
153 next unless yield(plugin)
156 "%s::%s::%s" % [ "MCollective", type.capitalize, plugin.capitalize ]
159 klasses.sort.uniq.each {|klass| loadclass(klass, true)}
162 # Loads a class from file by doing some simple search/replace
163 # on class names and then doing a require.
164 def self.loadclass(klass, squash_failures=false)
165 fname = klass.gsub("::", "/").downcase + ".rb"
167 Log.debug("Loading #{klass} from #{fname}")
170 rescue Exception => e
171 Log.error("Failed to load #{klass}: #{e}")
172 raise unless squash_failures
175 # Grep's over the plugin list and returns the list found
177 @plugins.keys.grep(regex)