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 => 'Revision number',
37 :arguments => ['--revision REVISION'],
41 :description => 'DEPRECATED - Use --revision instead',
42 :arguments => ['--iteration ITERATION'],
46 :description => 'Vendor name',
47 :arguments => ['--vendor VENDOR'],
51 :description => 'MCollective plugin path',
52 :arguments => ['--pluginpath PATH'],
56 :description => 'MCollective type (mcollective, pe-mcollective) that the packages depend on',
57 :arguments => ['--mcname NAME'],
61 :description => 'Version of MCollective that the packages depend on',
62 :arguments => ['--mcversion MCVERSION'],
66 :description => 'Adds a dependency to the plugin',
67 :arguments => ['--dependency DEPENDENCIES'],
71 :description => 'Package output format. Defaults to rpmpackage or debpackage',
72 :arguments => ['--format OUTPUTFORMAT'],
76 :description => 'Embed a signature in the package',
77 :arguments => ['--sign'],
81 :description => 'Template to use.',
82 :arguments => ['--template HELPTEMPLATE'],
86 :description => 'Plugin description',
87 :arguments => ['--description DESCRIPTION'],
91 :description => 'The author of the plugin',
92 :arguments => ['--author AUTHOR'],
96 :description => 'The license under which the plugin is distributed',
97 :arguments => ['--license LICENSE'],
101 :description => 'The version of the plugin',
102 :arguments => ['--pluginversion VERSION'],
106 :description => 'Url at which information about the plugin can be found',
107 :arguments => ['--url URL'],
111 :description => "The plugin's timeout",
112 :arguments => ['--timeout TIMEOUT'],
116 :description => 'Actions to be generated for an Agent Plugin',
117 :arguments => ['--actions [ACTIONS]'],
121 :description => 'Outputs to be generated for an Data Plugin',
122 :arguments => ['--outputs [OUTPUTS]'],
125 option :keep_artifacts,
126 :description => "Don't remove artifacts after building packages",
127 :arguments => ['--keep-artifacts'],
130 option :module_template,
131 :description => "Path to the template used by the modulepackager",
132 :arguments => ['--module-template PATH'],
135 # Handle alternative format that optparser can't parse.
136 def post_option_parser(configuration)
138 configuration[:action] = ARGV.delete_at(0)
140 configuration[:target] = ARGV.delete_at(0) || "."
142 if configuration[:action] == "generate"
143 unless ARGV[0] && ARGV[0].match(/(actions|outputs)=(.+)/i)
144 unless configuration[:pluginname]
145 configuration[:pluginname] = ARGV.delete_at(0)
151 ARGV.each do |argument|
152 if argument.match(/(actions|outputs)=(.+)/i)
153 configuration[$1.downcase.to_sym]= $2.split(",")
155 raise "Could not parse --arg '#{argument}'"
162 # Display info about plugin
164 plugin = prepare_plugin
165 packager = PluginPackager["#{configuration[:format].capitalize}Packager"]
166 packager.new(plugin).package_information
169 # Generate a plugin skeleton
171 raise "undefined plugin type. cannot generate plugin. valid types are 'agent' and 'data'" if configuration["target"] == '.'
173 unless configuration[:pluginname]
174 puts "No plugin name specified. Using 'new_plugin'"
175 configuration[:pluginname] = "new_plugin"
178 load_plugin_config_values
180 case configuration[:target].downcase
182 Generators::AgentGenerator.new(configuration[:pluginname], configuration[:actions], configuration[:pluginname],
183 configuration[:description], configuration[:author], configuration[:license],
184 configuration[:version], configuration[:url], configuration[:timeout])
186 raise "data plugin must have at least one output" unless configuration[:outputs]
187 Generators::DataGenerator.new(configuration[:pluginname], configuration[:outputs], configuration[:pluginname],
188 configuration[:description], configuration[:author], configuration[:license],
189 configuration[:version], configuration[:url], configuration[:timeout])
191 raise "invalid plugin type. cannot generate plugin '#{configuration[:target]}'"
197 if configuration[:sign] && Config.instance.pluginconf.include?("debian_packager.keyname")
198 configuration[:sign] = Config.instance.pluginconf["debian_packager.keyname"]
199 configuration[:sign] = "\"#{configuration[:sign]}\"" unless configuration[:sign].match(/\".*\"/)
202 plugin = prepare_plugin
203 (configuration[:pluginpath] = configuration[:pluginpath] + "/") if (configuration[:pluginpath] && !configuration[:pluginpath].match(/^.*\/$/))
204 packager = PluginPackager["#{configuration[:format].capitalize}Packager"]
205 packager.new(plugin, configuration[:pluginpath], configuration[:sign],
206 options[:verbose], configuration[:keep_artifacts],
207 configuration[:module_template]).create_packages
210 # Agents are just called 'agent' but newer plugin types are
211 # called plugin_plugintype for example facter_facts etc so
212 # this will first try the old way then the new way.
213 def load_plugin_ddl(plugin, type)
214 [plugin, "#{plugin}_#{type}"].each do |p|
215 ddl = DDL.new(p, type, false)
216 if ddl.findddlfile(p, type)
225 # Show application list and plugin help
227 known_plugin_types = [["Agents", :agent], ["Aggregate", :aggregate], ["Data Queries", :data], ["Discovery Methods", :discovery], ["Validator Plugins", :validator]]
229 if configuration.include?(:target) && configuration[:target] != "."
230 if configuration[:target] =~ /^(.+?)\/(.+)$/
231 ddl = load_plugin_ddl($2.to_sym, $1)
233 found_plugin_type = nil
235 known_plugin_types.each do |plugin_type|
236 PluginManager.find(plugin_type[1], "ddl").each do |ddl|
237 pluginname = ddl.gsub(/_#{plugin_type[1]}$/, "")
238 if pluginname == configuration[:target]
239 abort "Duplicate plugin name found, please specify a full path like agent/rpcutil" if found_plugin_type
240 found_plugin_type = plugin_type[1]
245 abort "Could not find a plugin named '%s' in any supported plugin type" % configuration[:target] unless found_plugin_type
246 ddl = load_plugin_ddl(configuration[:target], found_plugin_type)
250 puts ddl.help(configuration[:rpctemplate])
252 abort "Could not find a '%s' plugin named '%s'" % configuration[:target].split('/')
256 puts "Please specify a plugin. Available plugins are:"
261 known_plugin_types.each do |plugin_type|
262 puts "%s:" % plugin_type[0]
264 PluginManager.find(plugin_type[1], "ddl").each do |ddl|
266 help = DDL.new(ddl, plugin_type[1])
267 pluginname = ddl.gsub(/_#{plugin_type[1]}$/, "")
268 puts " %-25s %s" % [pluginname, help.meta[:description]]
270 load_errors << [plugin_type[1], ddl, e]
277 unless load_errors.empty?
278 puts "Plugin Load Errors:"
280 load_errors.each do |e|
281 puts " %-25s %s" % ["#{e[0]}/#{e[1]}", Util.colorize(:yellow, e[2])]
287 # Creates the correct package plugin object.
289 plugintype = set_plugin_type unless configuration[:plugintype]
290 configuration[:format] = "ospackage" unless configuration[:format]
291 PluginPackager.load_packagers
292 plugin_class = PluginPackager[configuration[:plugintype]]
294 if configuration[:dependency] && configuration[:dependency].size == 1
295 configuration[:dependency] = configuration[:dependency][0].split(" ")
296 elsif configuration[:dependency]
297 configuration[:dependency].map!{|dep| {:name => dep, :version => nil}}
300 mcdependency = {:mcname => configuration[:mcname], :mcversion => configuration[:mcversion]}
302 #Deprecation warning for --iteration
303 if configuration[:iteration]
304 puts 'Warning. The --iteration flag has been deprecated. Please use --revision instead.'
305 configuration[:revision] = configuration[:iteration] unless configuration[:revision]
308 plugin_class.new(configuration, mcdependency, plugintype)
311 def directory_for_type(type)
312 File.directory?(File.join(configuration[:target], type))
315 # Identify plugin type if not provided.
317 if directory_for_type("agent") || directory_for_type("application")
318 configuration[:plugintype] = "AgentDefinition"
320 elsif directory_for_type(plugintype = identify_plugin)
321 configuration[:plugintype] = "StandardDefinition"
324 raise RuntimeError, "target directory is not a valid mcollective plugin"
328 # If plugintype is StandardDefinition, identify which of the special
329 # plugin types we are dealing with based on directory structure.
330 # To keep it simple we limit it to one type per target directory.
332 plugintype = Dir.glob(File.join(configuration[:target], "*")).select do |file|
333 File.directory?(file) && file.match(/(connector|facts|registration|security|audit|pluginpackager|data|discovery|validator)/)
336 raise RuntimeError, "more than one plugin type detected in directory" if plugintype.size > 1
337 raise RuntimeError, "no plugins detected in directory" if plugintype.size < 1
339 File.basename(plugintype[0])
342 # Load preset metadata values from config if they are present
343 # This makes it possible to override metadata values in a local
344 # client config file.
346 # Example : plugin.metadata.license = Apache 2
347 def load_plugin_config_values
348 config = Config.instance
349 [:pluginname, :description, :author, :license, :version, :url, :timeout].each do |confoption|
350 configuration[confoption] = config.pluginconf["metadata.#{confoption}"] unless configuration[confoption]
355 abort "No action specified, please run 'mco help plugin' for help" unless configuration.include?(:action)
357 cmd = "#{configuration[:action]}_command"
362 abort "Invalid action #{configuration[:action]}, please run 'mco help plugin' for help."