module MCollective class Application::Plugin mco plugin info mco plugin doc mco plugin doc mco plugin generate agent [actions=val,val] mco plugin generate data [outputs=val,val] info : Display plugin information including package details. package : Create all available plugin packages. doc : Display documentation for a specific plugin. END_OF_USAGE option :pluginname, :description => 'Plugin name', :arguments => ['-n', '--name NAME'], :type => String option :postinstall, :description => 'Post install script', :arguments => ['--postinstall POSTINSTALL'], :type => String option :preinstall, :description => 'Pre install script', :arguments => ['--preinstall PREINSTALL'], :type => String option :revision, :description => 'Revision number', :arguments => ['--revision REVISION'], :type => String option :iteration, :description => 'DEPRECATED - Use --revision instead', :arguments => ['--iteration ITERATION'], :type => String option :vendor, :description => 'Vendor name', :arguments => ['--vendor VENDOR'], :type => String option :pluginpath, :description => 'MCollective plugin path', :arguments => ['--pluginpath PATH'], :type => String option :mcname, :description => 'MCollective type (mcollective, pe-mcollective) that the packages depend on', :arguments => ['--mcname NAME'], :type => String option :mcversion, :description => 'Version of MCollective that the packages depend on', :arguments => ['--mcversion MCVERSION'], :type => String option :dependency, :description => 'Adds a dependency to the plugin', :arguments => ['--dependency DEPENDENCIES'], :type => :array option :format, :description => 'Package output format. Defaults to rpmpackage or debpackage', :arguments => ['--format OUTPUTFORMAT'], :type => String option :sign, :description => 'Embed a signature in the package', :arguments => ['--sign'], :type => :boolean option :rpctemplate, :description => 'Template to use.', :arguments => ['--template HELPTEMPLATE'], :type => String option :description, :description => 'Plugin description', :arguments => ['--description DESCRIPTION'], :type => String option :author, :description => 'The author of the plugin', :arguments => ['--author AUTHOR'], :type => String option :license, :description => 'The license under which the plugin is distributed', :arguments => ['--license LICENSE'], :type => String option :version, :description => 'The version of the plugin', :arguments => ['--pluginversion VERSION'], :type => String option :url, :description => 'Url at which information about the plugin can be found', :arguments => ['--url URL'], :type => String option :timeout, :description => "The plugin's timeout", :arguments => ['--timeout TIMEOUT'], :type => Integer option :actions, :description => 'Actions to be generated for an Agent Plugin', :arguments => ['--actions [ACTIONS]'], :type => Array option :outputs, :description => 'Outputs to be generated for an Data Plugin', :arguments => ['--outputs [OUTPUTS]'], :type => Array option :keep_artifacts, :description => "Don't remove artifacts after building packages", :arguments => ['--keep-artifacts'], :type => :boolean option :module_template, :description => "Path to the template used by the modulepackager", :arguments => ['--module-template PATH'], :type => String # Handle alternative format that optparser can't parse. def post_option_parser(configuration) if ARGV.length >= 1 configuration[:action] = ARGV.delete_at(0) configuration[:target] = ARGV.delete_at(0) || "." if configuration[:action] == "generate" unless ARGV[0] && ARGV[0].match(/(actions|outputs)=(.+)/i) unless configuration[:pluginname] configuration[:pluginname] = ARGV.delete_at(0) else ARGV.delete_at(0) end end ARGV.each do |argument| if argument.match(/(actions|outputs)=(.+)/i) configuration[$1.downcase.to_sym]= $2.split(",") else raise "Could not parse --arg '#{argument}'" end end end end end # Display info about plugin def info_command plugin = prepare_plugin packager = PluginPackager["#{configuration[:format].capitalize}Packager"] packager.new(plugin).package_information end # Generate a plugin skeleton def generate_command raise "undefined plugin type. cannot generate plugin. valid types are 'agent' and 'data'" if configuration["target"] == '.' unless configuration[:pluginname] puts "No plugin name specified. Using 'new_plugin'" configuration[:pluginname] = "new_plugin" end load_plugin_config_values case configuration[:target].downcase when 'agent' Generators::AgentGenerator.new(configuration[:pluginname], configuration[:actions], configuration[:pluginname], configuration[:description], configuration[:author], configuration[:license], configuration[:version], configuration[:url], configuration[:timeout]) when 'data' raise "data plugin must have at least one output" unless configuration[:outputs] Generators::DataGenerator.new(configuration[:pluginname], configuration[:outputs], configuration[:pluginname], configuration[:description], configuration[:author], configuration[:license], configuration[:version], configuration[:url], configuration[:timeout]) else raise "invalid plugin type. cannot generate plugin '#{configuration[:target]}'" end end # Package plugin def package_command if configuration[:sign] && Config.instance.pluginconf.include?("debian_packager.keyname") configuration[:sign] = Config.instance.pluginconf["debian_packager.keyname"] configuration[:sign] = "\"#{configuration[:sign]}\"" unless configuration[:sign].match(/\".*\"/) end plugin = prepare_plugin (configuration[:pluginpath] = configuration[:pluginpath] + "/") if (configuration[:pluginpath] && !configuration[:pluginpath].match(/^.*\/$/)) packager = PluginPackager["#{configuration[:format].capitalize}Packager"] packager.new(plugin, configuration[:pluginpath], configuration[:sign], options[:verbose], configuration[:keep_artifacts], configuration[:module_template]).create_packages end # Agents are just called 'agent' but newer plugin types are # called plugin_plugintype for example facter_facts etc so # this will first try the old way then the new way. def load_plugin_ddl(plugin, type) [plugin, "#{plugin}_#{type}"].each do |p| ddl = DDL.new(p, type, false) if ddl.client_activated? && ddl.findddlfile(p, type) ddl.loadddlfile return ddl end end return nil end # Show application list and plugin help def doc_command known_plugin_types = [ ["Agents", :agent], ["Aggregate", :aggregate], ["Connectors", :connector], ["Data Queries", :data], ["Discovery Methods", :discovery], ["Validator Plugins", :validator] ] if configuration.include?(:target) && configuration[:target] != "." if configuration[:target] =~ /^(.+?)\/(.+)$/ ddl = load_plugin_ddl($2.to_sym, $1) else found_plugin_type = nil known_plugin_types.each do |plugin_type| PluginManager.find(plugin_type[1], "ddl").each do |ddl| pluginname = ddl.gsub(/_#{plugin_type[1]}$/, "") if pluginname == configuration[:target] abort "Duplicate plugin name found, please specify a full path like agent/rpcutil" if found_plugin_type found_plugin_type = plugin_type[1] end end end abort "Could not find a plugin named '%s' in any supported plugin type" % configuration[:target] unless found_plugin_type ddl = load_plugin_ddl(configuration[:target], found_plugin_type) end if ddl puts ddl.help(configuration[:rpctemplate]) else abort "Could not find a '%s' plugin named '%s'" % configuration[:target].split('/') end else puts "Please specify a plugin. Available plugins are:" puts load_errors = [] known_plugin_types.each do |plugin_type| puts "%s:" % plugin_type[0] PluginManager.find(plugin_type[1], "ddl").each do |ddl| begin help = DDL.new(ddl, plugin_type[1], false) next unless help.client_activated? help.loadddlfile pluginname = ddl.gsub(/_#{plugin_type[1]}$/, "") puts " %-25s %s" % [pluginname, help.meta[:description]] rescue => e load_errors << [plugin_type[1], ddl, e] end end puts end unless load_errors.empty? puts "Plugin Load Errors:" load_errors.each do |e| puts " %-25s %s" % ["#{e[0]}/#{e[1]}", Util.colorize(:yellow, e[2])] end end end end # Creates the correct package plugin object. def prepare_plugin plugintype = set_plugin_type unless configuration[:plugintype] configuration[:format] = "forge" unless configuration[:format] PluginPackager.load_packagers plugin_class = PluginPackager[configuration[:plugintype]] if configuration[:dependency] && configuration[:dependency].size == 1 configuration[:dependency] = configuration[:dependency][0].split(" ") elsif configuration[:dependency] configuration[:dependency].map!{|dep| {:name => dep, :version => nil}} end mcdependency = { :mcname => configuration[:mcname], :mcversion => configuration[:mcversion] } # Deprecation warning for --iteration if configuration[:iteration] puts 'Warning. The --iteration flag has been deprecated. Please use --revision instead.' configuration[:revision] = configuration[:iteration] unless configuration[:revision] end plugin_class.new(configuration, mcdependency, plugintype) end def plugin_directory_exists?(plugin_type) File.directory?(File.join(PluginPackager.get_plugin_path(configuration[:target]), plugin_type)) end # Identify plugin type if not provided. def set_plugin_type if plugin_directory_exists?("agent") || plugin_directory_exists?("application") configuration[:plugintype] = "AgentDefinition" return "Agent" elsif plugin_directory_exists?(plugintype = identify_plugin) configuration[:plugintype] = "StandardDefinition" return plugintype else raise RuntimeError, "target directory is not a valid mcollective plugin" end end # If plugintype is StandardDefinition, identify which of the special # plugin types we are dealing with based on directory structure. # To keep it simple we limit it to one type per target directory. # Return the name of the type of plugin as a string def identify_plugin plugintype = Dir.glob(File.join(configuration[:target], "*")).select do |file| File.directory?(file) && file.match(/(connector|facts|registration|security|audit|pluginpackager|data|discovery|validator)/) end raise RuntimeError, "more than one plugin type detected in directory" if plugintype.size > 1 raise RuntimeError, "no plugins detected in directory" if plugintype.size < 1 File.basename(plugintype[0]) end # Load preset metadata values from config if they are present # This makes it possible to override metadata values in a local # client config file. # # Example : plugin.metadata.license = Apache 2 def load_plugin_config_values config = Config.instance [:pluginname, :description, :author, :license, :version, :url, :timeout].each do |confoption| configuration[confoption] = config.pluginconf["metadata.#{confoption}"] unless configuration[confoption] end end def main abort "No action specified, please run 'mco help plugin' for help" unless configuration.include?(:action) cmd = "#{configuration[:action]}_command" if respond_to? cmd send cmd else abort "Invalid action #{configuration[:action]}, please run 'mco help plugin' for help." end end end end