3 # The base class for all kinds of DDL files. DDL files when
4 # run gets parsed and builds up a hash of the basic primitive
5 # types, ideally restricted so it can be converted to JSON though
6 # today there are some Ruby Symbols in them which might be fixed
9 # The Hash being built should be stored in @entities, the format
10 # is generally not prescribed but there's a definite feel to how
11 # DDL files look so study the agent and discovery ones to see how
12 # the structure applies to very different use cases.
14 # For every plugin type you should have a single word name - that
15 # corresponds to the directory in the libdir where these plugins
16 # live. If you need anything above and beyond 'metadata' in your
17 # plugin DDL then add a PlugintypeDDL class here and add your
18 # specific behaviors to those.
22 attr_reader :meta, :entities, :pluginname, :plugintype, :usage, :requirements
24 def initialize(plugin, plugintype=:agent, loadddl=true)
28 @config = Config.instance
30 @plugintype = plugintype.to_sym
33 loadddlfile if loadddl
36 # Generates help using the template based on the data
37 # created with metadata and input.
39 # If no template name is provided one will be chosen based
40 # on the plugin type. If the provided template path is
41 # not absolute then the template will be loaded either from
42 # the config dir and if that does not exist, default to
44 def help(template=nil)
45 template = template_for_plugintype unless template
46 template = Util.templatepath(template) unless Util.absolute_path?(template)
48 template = File.read(template)
52 unless template == "metadata-help.erb"
53 metadata_template = Util.templatepath("metadata-help.erb")
54 metadata_template = File.read(metadata_template)
55 metastring = ERB.new(metadata_template, 0, '%')
56 metastring = metastring.result(binding)
59 erb = ERB.new(template, 0, '%')
67 def template_for_plugintype
72 if File.exists?(Util.templatepath("#{@plugintype}-help.erb"))
73 return "#{@plugintype}-help.erb"
75 # Default help template gets loaded if plugintype-help does not exist.
76 return "metadata-help.erb"
82 if ddlfile = findddlfile
83 instance_eval(File.read(ddlfile), ddlfile, 1)
85 raise_code(:PLMC40, "Can't find DDL for %{type} plugin '%{name}'", :debug, :type => @plugintype, :name => @pluginname)
89 def findddlfile(ddlname=nil, ddltype=nil)
90 ddlname = @pluginname unless ddlname
91 ddltype = @plugintype unless ddltype
93 @config.libdir.each do |libdir|
94 ddlfile = File.join([libdir, "mcollective", ddltype.to_s, "#{ddlname}.ddl"])
95 if File.exist?(ddlfile)
96 log_code(:PLMC18, "Found %{ddlname} ddl at %{ddlfile}", :debug, :ddlname => ddlname, :ddlfile => ddlfile)
103 def validate_requirements
104 if requirement = @requirements[:mcollective]
105 if Util.mcollective_version == "@DEVELOPMENT_VERSION@"
106 log_code(:PLMC19, "DDL requirements validation being skipped in development", :warn)
110 if Util.versioncmp(Util.mcollective_version, requirement) < 0
111 DDL.validation_fail!(:PLMC20, "%{type} plugin '%{name}' requires MCollective version %{requirement} or newer", :debug, :type => @plugintype.to_s.capitalize, :name => @pluginname, :requirement => requirement)
118 # validate strings, lists and booleans, we'll add more types of validators when
119 # all the use cases are clear
121 # only does validation for arguments actually given, since some might
122 # be optional. We validate the presense of the argument earlier so
123 # this is a safe assumption, just to skip them.
125 # :string can have maxlength and regex. A maxlength of 0 will bypasss checks
126 # :list has a array of valid values
127 def validate_input_argument(input, key, argument)
128 Validator.load_validators
130 case input[key][:type]
132 Validator.validate(argument, :string)
134 Validator.length(argument, input[key][:maxlength].to_i)
136 Validator.validate(argument, input[key][:validation])
139 Validator.validate(argument, input[key][:list])
142 Validator.validate(argument, input[key][:type])
147 DDL.validation_fail!(:PLMC21, "Cannot validate input '%{input}': %{error}", :debug, :input => key, :error => e.to_s)
150 # Registers an input argument for a given action
152 # See the documentation for action for how to use this
153 def input(argument, properties)
154 raise_code(:PLMC22, "Cannot determine what entity input '%{entity}' belongs to", :error, :entity => @current_entity) unless @current_entity
156 entity = @current_entity
158 [:prompt, :description, :type].each do |arg|
159 raise_code(:PLMC23, "Input needs a :%{property} property", :debug, :property => arg) unless properties.include?(arg)
162 @entities[entity][:input][argument] = {:prompt => properties[:prompt],
163 :description => properties[:description],
164 :type => properties[:type],
165 :default => properties[:default],
166 :optional => properties[:optional]}
168 case properties[:type]
170 raise "Input type :string needs a :validation argument" unless properties.include?(:validation)
171 raise "Input type :string needs a :maxlength argument" unless properties.include?(:maxlength)
173 @entities[entity][:input][argument][:validation] = properties[:validation]
174 @entities[entity][:input][argument][:maxlength] = properties[:maxlength]
177 raise "Input type :list needs a :list argument" unless properties.include?(:list)
179 @entities[entity][:input][argument][:list] = properties[:list]
183 # Registers an output argument for a given action
185 # See the documentation for action for how to use this
186 def output(argument, properties)
187 raise "Cannot figure out what action input #{argument} belongs to" unless @current_entity
188 raise "Output #{argument} needs a description argument" unless properties.include?(:description)
189 raise "Output #{argument} needs a display_as argument" unless properties.include?(:display_as)
191 action = @current_entity
193 @entities[action][:output][argument] = {:description => properties[:description],
194 :display_as => properties[:display_as],
195 :default => properties[:default]}
198 def requires(requirement)
199 raise "Requirement should be a hash in the form :item => 'requirement'" unless requirement.is_a?(Hash)
201 valid_requirements = [:mcollective]
203 requirement.keys.each do |key|
204 unless valid_requirements.include?(key)
205 raise "Requirement %s is not a valid requirement, only %s is supported" % [key, valid_requirements.join(", ")]
208 @requirements[key] = requirement[key]
211 validate_requirements
214 # Registers meta data for the introspection hash
216 [:name, :description, :author, :license, :version, :url, :timeout].each do |arg|
217 raise "Metadata needs a :#{arg} property" unless meta.include?(arg)