module Puppet class Parameter < Puppet::Element class << self attr_reader :validater, :munger, :name, :default attr_accessor :ismetaparameter, :element # Define the default value for a given parameter or parameter. This # means that 'nil' is an invalid default value. This defines # the 'default' instance method. def defaultto(value = nil, &block) if block define_method(:default, &block) else if value.nil? raise Puppet::DevError, "Either a default value or block must be provided" end define_method(:default) do value end end end def nodefault if public_method_defined? :default undef_method :default end end # Store documentation for this parameter. def desc(str) @doc = str end # This is how we munge the value. Basically, this is our # opportunity to convert the value from one form into another. def munge(&block) # I need to wrap the unsafe version in begin/rescue parameterments, # but if I directly call the block then it gets bound to the # class's context, not the instance's, thus the two methods, # instead of just one. define_method(:unsafe_munge, &block) define_method(:munge) do |*args| begin unsafe_munge(*args) rescue Puppet::Error => detail Puppet.debug "Reraising %s" % detail raise rescue => detail if Puppet[:debug] puts detail.backtrace end raise Puppet::DevError, "Munging failed for class %s: %s" % [self.name, detail] end end #@munger = block end #def inspect # "Parameter(#{self.name})" #end # Mark whether we're the namevar. def isnamevar @isnamevar = true @required = true end # Is this parameter the namevar? Defaults to false. def isnamevar? if defined? @isnamevar return @isnamevar else return false end end # This parameter is required. def isrequired @required = true end # Is this parameter required? Defaults to false. def required? if defined? @required return @required else return false end end def to_s if self.ismetaparameter "Puppet::Type::" + @name.to_s.capitalize else self.element.to_s + @name.to_s.capitalize end end # Verify that we got a good value def validate(&block) #@validater = block define_method(:unsafe_validate, &block) define_method(:validate) do |*args| begin unsafe_validate(*args) rescue ArgumentError, Puppet::Error, TypeError raise rescue => detail if Puppet[:debug] puts detail.backtrace end raise Puppet::DevError, "Validate method failed for class %s: %s" % [self.name, detail] end end end # Define a new value for our parameter. def newvalues(*names) @parametervalues ||= [] names.each { |name| if @parametervalues.include?(name) Puppet.warning "%s already has a value for %s" % [name, name] end @parametervalues << name } end def aliasvalue(name, other) @parametervalues ||= [] unless @parametervalues.include?(other) raise Puppet::DevError, "Cannot alias nonexistent value %s" % other end @aliasvalues ||= {} @aliasvalues[name] = other end def alias(name) @aliasvalues[name] end # Return the list of valid values. def values @parametervalues ||= [] @aliasvalues ||= {} #[@aliasvalues.keys, @parametervalues.keys].flatten if @parametervalues.is_a? Array return @parametervalues.dup elsif @parametervalues.is_a? Hash return @parametervalues.keys else return [] end end end # Just a simple method to proxy instance methods to class methods def self.proxymethods(*values) values.each { |val| eval "def #{val}; self.class.#{val}; end" } end # And then define one of these proxies for each method in our # ParamHandler class. proxymethods("required?", "isnamevar?") attr_accessor :parent def devfail(msg) self.fail(Puppet::DevError, msg) end def fail(*args) type = nil if args[0].is_a?(Class) type = args.shift else type = Puppet::Error end error = type.new(args.join(" ")) if defined? @parent and @parent and @parent.line error.line = @parent.line end if defined? @parent and @parent and @parent.file error.file = @parent.file end raise error end # Log a message using the parent's log level. def log(msg) unless @parent[:loglevel] p @parent self.devfail "Parent %s has no loglevel" % @parent.name end Puppet::Log.create( :level => @parent[:loglevel], :message => msg, :source => self ) end # each parameter class must define the name() method, and parameter # instances do not change that name this implicitly means that a given # object can only have one parameter instance of a given parameter # class def name return self.class.name end # for testing whether we should actually do anything def noop unless defined? @noop @noop = false end tmp = @noop || self.parent.noop || Puppet[:noop] || false #debug "noop is %s" % tmp return tmp end # return the full path to us, for logging and rollback; not currently # used def path return [@parent.path, self.name].join("/") end # If the specified value is allowed, then munge appropriately. munge do |value| if self.class.values.empty? # This parameter isn't using defined values to do its work. return value end intern = value.to_s.intern # If it's a valid value, always return it as a symbol. if self.class.values.include?(intern) retval = intern elsif other = self.class.alias(intern) self.info "returning alias %s for %s" % [other, intern] retval = other else retval = value end retval end # Verify that the passed value is valid. validate do |value| values = self.class.values if values.empty? # This parameter isn't using defined values to do its work. return end unless value.is_a?(Symbol) value = value.to_s.intern end unless values.include?(value) or self.class.alias(value) self.fail "Invalid '%s' value '%s'. Valid values are '%s'" % [self.class.name, value, values.join(", ")] end end # This should only be called for parameters, but go ahead and make # it possible to call for states, too. def value if self.is_a?(Puppet::State) # We should return the 'is' value if there's not 'should' value. # This might be bad, though, because the 'should' method # knows whether to return an array or not and that info is # not exposed, and the 'is' value could be a symbol. I can't # seem to create a test in which this is a problem, but that doesn't # mean it's not one. if self.should return self.should else return self.is end else if defined? @value return @value else return nil end end end # Store the value provided. All of the checking should possibly be # late-binding (e.g., users might not exist when the value is assigned # but might when it is asked for). def value=(value) # If we're a parameter, just hand the processing off to the should # method. if self.is_a?(Puppet::State) return self.should = value end if respond_to?(:validate) validate(value) end if respond_to?(:munge) value = munge(value) end @value = value end def inspect s = "Parameter(%s = %s" % [self.name, self.value || "nil"] if defined? @parent s += ", @parent = %s)" % @parent else s += ")" end end def to_s s = "Parameter(%s)" % self.name end end end