X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=lib%2Fmcollective%2Fddl.rb;fp=lib%2Fmcollective%2Fddl.rb;h=dc2410dbc49f69a35f68d2b697d5a6cb6d8df085;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/lib/mcollective/ddl.rb b/lib/mcollective/ddl.rb new file mode 100644 index 0000000..dc2410d --- /dev/null +++ b/lib/mcollective/ddl.rb @@ -0,0 +1,124 @@ +module MCollective + # A set of classes that helps create data description language files + # for plugins. You can define meta data, actions, input and output + # describing the behavior of your agent or other plugins + # + # DDL files are used for input validation, constructing outputs, + # producing online help, informing the various display routines and + # so forth. + # + # A sample DDL for an agent be seen below, you'd put this in your agent + # dir as .ddl + # + # metadata :name => "SimpleRPC Service Agent", + # :description => "Agent to manage services using the Puppet service provider", + # :author => "R.I.Pienaar", + # :license => "GPLv2", + # :version => "1.1", + # :url => "http://mcollective-plugins.googlecode.com/", + # :timeout => 60 + # + # action "status", :description => "Gets the status of a service" do + # display :always + # + # input :service, + # :prompt => "Service Name", + # :description => "The service to get the status for", + # :type => :string, + # :validation => '^[a-zA-Z\-_\d]+$', + # :optional => true, + # :maxlength => 30 + # + # output :status, + # :description => "The status of service", + # :display_as => "Service Status" + # end + # + # There are now many types of DDL and ultimately all pugins should have + # DDL files. The code is organized so that any plugin type will magically + # just work - they will be an instane of Base which has #metadata and a few + # common cases. + # + # For plugin types that require more specific behaviors they can just add a + # class here that inherits from Base and add their specific behavior. + # + # Base defines a specific behavior for input, output and metadata which we'd + # like to keep standard across plugin types so do not completely override the + # behavior of input. The methods are written that they will gladly store extra + # content though so you add, do not remove. See the AgentDDL class for an example + # where agents want a :required argument to be always set. + module DDL + autoload :Base, "mcollective/ddl/base" + autoload :AgentDDL, "mcollective/ddl/agentddl" + autoload :DataDDL, "mcollective/ddl/dataddl" + autoload :DiscoveryDDL, "mcollective/ddl/discoveryddl" + + extend Translatable + + # There used to be only one big nasty DDL class with a bunch of mashed + # together behaviors. It's been around for ages and we would rather not + # ask all the users to change their DDL.new calls to some other factory + # method that would have this exact same behavior. + # + # So we override the behavior of #new which is a hugely sucky thing to do + # but ultimately it's what would be least disrupting to code out there + # today. We did though change DDL to a module to make it possibly a + # little less suprising, possibly. + def self.new(*args, &blk) + load_and_cache(*args) + end + + def self.load_and_cache(*args) + Cache.setup(:ddl, 300) + + plugin = args.first + args.size > 1 ? type = args[1].to_s : type = "agent" + path = "%s/%s" % [type, plugin] + + begin + ddl = Cache.read(:ddl, path) + rescue + begin + klass = DDL.const_get("%sDDL" % type.capitalize) + rescue NameError + klass = Base + end + + ddl = Cache.write(:ddl, path, klass.new(*args)) + end + + return ddl + end + + # As we're taking arguments on the command line we need a + # way to input booleans, true on the cli is a string so this + # method will take the ddl, find all arguments that are supposed + # to be boolean and if they are the strings "true"/"yes" or "false"/"no" + # turn them into the matching boolean + def self.string_to_boolean(val) + return true if ["true", "t", "yes", "y", "1"].include?(val.downcase) + return false if ["false", "f", "no", "n", "0"].include?(val.downcase) + + raise_code(:PLMC17, "%{value} does not look like a boolean argument", :debug, :value => val) + end + + # a generic string to number function, if a number looks like a float + # it turns it into a float else an int. This is naive but should be sufficient + # for numbers typed on the cli in most cases + def self.string_to_number(val) + return val.to_f if val =~ /^\d+\.\d+$/ + return val.to_i if val =~ /^\d+$/ + + raise_code(:PLMC16, "%{value} does not look like a numeric value", :debug, :value => val) + end + + # Various DDL implementations will validate and raise on error, this is a + # utility method to correctly setup a DDLValidationError exceptions and raise them + def self.validation_fail!(code, default, level, args={}) + exception = DDLValidationError.new(code, default, level, args) + exception.set_backtrace caller + + raise exception + end + end +end