2 class Application::Plugin<Application
4 exclude_argument_sections "common", "filter", "rpc"
6 description "MCollective Plugin Application"
8 mco plugin package [options] <directory>
9 mco plugin info <directory>
10 mco plugin doc <plugin>
11 mco plugin doc <type/plugin>
12 mco plugin generate agent <pluginname> [actions=val,val]
13 mco plugin generate data <pluginname> [outputs=val,val]
15 info : Display plugin information including package details.
16 package : Create all available plugin packages.
17 doc : Display documentation for a specific plugin.
21 :description => "Plugin name",
22 :arguments => ["-n", "--name NAME"],
26 :description => "Post install script",
27 :arguments => ["--postinstall POSTINSTALL"],
31 :description => "Pre install script",
32 :arguments => ["--preinstall PREINSTALL"],
36 :description => "Iteration number",
37 :arguments => ["--iteration ITERATION"],
41 :description => "Vendor name",
42 :arguments => ["--vendor VENDOR"],
46 :description => "MCollective plugin path",
47 :arguments => ["--pluginpath PATH"],
51 :description => "MCollective type (mcollective, pe-mcollective) that the packages depend on",
52 :arguments => ["--mcname NAME"],
56 :description => "Version of MCollective that the packages depend on",
57 :arguments => "--mcversion MCVERSION",
61 :description => "Adds a dependency to the plugin",
62 :arguments => ["--dependency DEPENDENCIES"],
66 :description => "Package output format. Defaults to rpmpackage or debpackage",
67 :arguments => ["--format OUTPUTFORMAT"],
71 :description => "Embed a signature in the package",
72 :arguments => ["--sign"],
76 :description => "Template to use.",
77 :arguments => ["--template HELPTEMPLATE"],
81 :description => "Plugin description",
82 :arguments => ["--description DESCRIPTION"],
86 :description => "The author of the plugin",
87 :arguments => ["--author AUTHOR"],
91 :description => "The license under which the plugin is distributed",
92 :arguments => ["--license LICENSE"],
96 :description => "The version of the plugin",
97 :arguments => ["--pluginversion VERSION"],
101 :description => "Url at which information about the plugin can be found",
102 :arguments => ["--url URL"],
106 :description => "The plugin's timeout",
107 :arguments => ["--timeout TIMEOUT"],
111 :description => "Actions to be generated for an Agent Plugin",
112 :arguments => ["--actions [ACTIONS]"],
116 :description => "Outputs to be generated for an Data Plugin",
117 :arguments => ["--outputs [OUTPUTS]"],
120 # Handle alternative format that optparser can't parse.
121 def post_option_parser(configuration)
123 configuration[:action] = ARGV.delete_at(0)
125 configuration[:target] = ARGV.delete_at(0) || "."
127 if configuration[:action] == "generate"
128 unless ARGV[0] && ARGV[0].match(/(actions|outputs)=(.+)/i)
129 unless configuration[:pluginname]
130 configuration[:pluginname] = ARGV.delete_at(0)
136 ARGV.each do |argument|
137 if argument.match(/(actions|outputs)=(.+)/i)
138 configuration[$1.downcase.to_sym]= $2.split(",")
140 raise "Could not parse --arg '#{argument}'"
147 # Display info about plugin
149 plugin = prepare_plugin
150 packager = PluginPackager["#{configuration[:format].capitalize}Packager"]
151 packager.new(plugin).package_information
154 # Generate a plugin skeleton
156 raise "undefined plugin type. cannot generate plugin. valid types are 'agent' and 'data'" if configuration["target"] == '.'
158 unless configuration[:pluginname]
159 puts "No plugin name specified. Using 'new_plugin'"
160 configuration[:pluginname] = "new_plugin"
163 load_plugin_config_values
165 case configuration[:target].downcase
167 Generators::AgentGenerator.new(configuration[:pluginname], configuration[:actions], configuration[:pluginname],
168 configuration[:description], configuration[:author], configuration[:license],
169 configuration[:version], configuration[:url], configuration[:timeout])
171 raise "data plugin must have at least one output" unless configuration[:outputs]
172 Generators::DataGenerator.new(configuration[:pluginname], configuration[:outputs], configuration[:pluginname],
173 configuration[:description], configuration[:author], configuration[:license],
174 configuration[:version], configuration[:url], configuration[:timeout])
176 raise "invalid plugin type. cannot generate plugin '#{configuration[:target]}'"
182 if configuration[:sign] && Config.instance.pluginconf.include?("debian_packager.keyname")
183 configuration[:sign] = Config.instance.pluginconf["debian_packager.keyname"]
184 configuration[:sign] = "\"#{configuration[:sign]}\"" unless configuration[:sign].match(/\".*\"/)
187 plugin = prepare_plugin
188 (configuration[:pluginpath] = configuration[:pluginpath] + "/") if (configuration[:pluginpath] && !configuration[:pluginpath].match(/^.*\/$/))
189 packager = PluginPackager["#{configuration[:format].capitalize}Packager"]
190 packager.new(plugin, configuration[:pluginpath], configuration[:sign], options[:verbose]).create_packages
193 # Agents are just called 'agent' but newer plugin types are
194 # called plugin_plugintype for example facter_facts etc so
195 # this will first try the old way then the new way.
196 def load_plugin_ddl(plugin, type)
197 [plugin, "#{plugin}_#{type}"].each do |p|
198 ddl = DDL.new(p, type, false)
199 if ddl.findddlfile(p, type)
206 # Show application list and plugin help
208 known_plugin_types = [["Agents", :agent], ["Data Queries", :data], ["Discovery Methods", :discovery], ["Validator Plugins", :validator]]
210 if configuration.include?(:target) && configuration[:target] != "."
211 if configuration[:target] =~ /^(.+?)\/(.+)$/
212 ddl = load_plugin_ddl($2.to_sym, $1)
214 found_plugin_type = nil
216 known_plugin_types.each do |plugin_type|
217 PluginManager.find(plugin_type[1], "ddl").each do |ddl|
218 pluginname = ddl.gsub(/_#{plugin_type[1]}$/, "")
219 if pluginname == configuration[:target]
220 abort "Duplicate plugin name found, please specify a full path like agent/rpcutil" if found_plugin_type
221 found_plugin_type = plugin_type[1]
226 abort "Could not find a plugin named %s in any supported plugin type" % configuration[:target] unless found_plugin_type
228 ddl = load_plugin_ddl(configuration[:target], found_plugin_type)
231 puts ddl.help(configuration[:rpctemplate])
233 puts "Please specify a plugin. Available plugins are:"
238 known_plugin_types.each do |plugin_type|
239 puts "%s:" % plugin_type[0]
241 PluginManager.find(plugin_type[1], "ddl").each do |ddl|
243 help = DDL.new(ddl, plugin_type[1])
244 pluginname = ddl.gsub(/_#{plugin_type[1]}$/, "")
245 puts " %-25s %s" % [pluginname, help.meta[:description]]
247 load_errors << [plugin_type[1], ddl, e]
254 unless load_errors.empty?
255 puts "Plugin Load Errors:"
257 load_errors.each do |e|
258 puts " %-25s %s" % ["#{e[0]}/#{e[1]}", Util.colorize(:yellow, e[2])]
264 # Creates the correct package plugin object.
266 plugintype = set_plugin_type unless configuration[:plugintype]
267 configuration[:format] = "ospackage" unless configuration[:format]
268 PluginPackager.load_packagers
269 plugin_class = PluginPackager[configuration[:plugintype]]
270 configuration[:dependency] = configuration[:dependency][0].split(" ") if configuration[:dependency] && configuration[:dependency].size == 1
271 configuration[:dependency].map!{|dep| {:name => dep, :version => nil}} if configuration[:dependency]
272 mcdependency = {:mcname => configuration[:mcname], :mcversion => configuration[:mcversion]}
274 plugin_class.new(configuration[:target], configuration[:pluginname],
275 configuration[:vendor], configuration[:preinstall],
276 configuration[:postinstall], configuration[:iteration],
277 configuration[:dependency], mcdependency , plugintype)
280 def directory_for_type(type)
281 File.directory?(File.join(configuration[:target], type))
284 # Identify plugin type if not provided.
286 if directory_for_type("agent") || directory_for_type("application")
287 configuration[:plugintype] = "AgentDefinition"
289 elsif directory_for_type(plugintype = identify_plugin)
290 configuration[:plugintype] = "StandardDefinition"
293 raise RuntimeError, "target directory is not a valid mcollective plugin"
297 # If plugintype is StandardDefinition, identify which of the special
298 # plugin types we are dealing with based on directory structure.
299 # To keep it simple we limit it to one type per target directory.
301 plugintype = Dir.glob(File.join(configuration[:target], "*")).select do |file|
302 File.directory?(file) && file.match(/(connector|facts|registration|security|audit|pluginpackager|data|discovery|validator)/)
305 raise RuntimeError, "more than one plugin type detected in directory" if plugintype.size > 1
306 raise RuntimeError, "no plugins detected in directory" if plugintype.size < 1
308 File.basename(plugintype[0])
311 # Load preset metadata values from config if they are present
312 # This makes it possible to override metadata values in a local
313 # client config file.
315 # Example : plugin.metadata.license = Apache 2
316 def load_plugin_config_values
317 config = Config.instance
318 [:pluginname, :description, :author, :license, :version, :url, :timeout].each do |confoption|
319 configuration[confoption] = config.pluginconf["metadata.#{confoption}"] unless configuration[confoption]
324 abort "No action specified, please run 'mco help plugin' for help" unless configuration.include?(:action)
326 cmd = "#{configuration[:action]}_command"
331 abort "Invalid action #{configuration[:action]}, please run 'mco help plugin' for help."