X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=plugins%2Fmcollective%2Fapplication%2Fplugin.rb;fp=plugins%2Fmcollective%2Fapplication%2Fplugin.rb;h=c9afaaa4a978a07052916e22b281c1fc29e9d3a3;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/plugins/mcollective/application/plugin.rb b/plugins/mcollective/application/plugin.rb new file mode 100644 index 0000000..c9afaaa --- /dev/null +++ b/plugins/mcollective/application/plugin.rb @@ -0,0 +1,335 @@ +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 :iteration, + :description => "Iteration number", + :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 + + # 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]).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.findddlfile(p, type) + ddl.loadddlfile + return ddl + end + end + end + + # Show application list and plugin help + def doc_command + known_plugin_types = [["Agents", :agent], ["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 + + puts ddl.help(configuration[:rpctemplate]) + 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]) + 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] = "ospackage" unless configuration[:format] + PluginPackager.load_packagers + plugin_class = PluginPackager[configuration[:plugintype]] + configuration[:dependency] = configuration[:dependency][0].split(" ") if configuration[:dependency] && configuration[:dependency].size == 1 + configuration[:dependency].map!{|dep| {:name => dep, :version => nil}} if configuration[:dependency] + mcdependency = {:mcname => configuration[:mcname], :mcversion => configuration[:mcversion]} + + plugin_class.new(configuration[:target], configuration[:pluginname], + configuration[:vendor], configuration[:preinstall], + configuration[:postinstall], configuration[:iteration], + configuration[:dependency], mcdependency , plugintype) + end + + def directory_for_type(type) + File.directory?(File.join(configuration[:target], type)) + end + + # Identify plugin type if not provided. + def set_plugin_type + if directory_for_type("agent") || directory_for_type("application") + configuration[:plugintype] = "AgentDefinition" + return "Agent" + elsif directory_for_type(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. + 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