Class: Puppet::Property Abstract

Inherits:
Parameter show all
Defined in:
lib/puppet/property.rb,
lib/puppet/type/zpool.rb,
lib/puppet/property/list.rb,
lib/puppet/property/keyvalue.rb,
lib/puppet/property/ordered_list.rb

Overview

This class is abstract.
Note:

Properties of Types are expressed using subclasses of this class. Such a class describes one named property of a particular Type (as opposed to describing a type of property in general). This limits the use of one (concrete) property class instance to occur only once for a given type's inheritance chain. An instance of a Property class is the value holder of one instance of the resource type (e.g. the mode of a file resource instance). A Property class may server as the superclass (parent) of another; e.g. a Size property that describes handling of measurements such as kb, mb, gb. If a type requires two different size measurements it requires one concrete class per such measure; e.g. MinSize (:parent => Size), and MaxSize (:parent => Size).

The Property class is the implementation of a resource's attributes of property kind. A Property is a specialized Resource Type Parameter that has both an 'is' (current) state, and a 'should' (wanted state). However, even if this is conceptually true, the current is value is obtained by asking the associated provider for the value, and hence it is not actually part of a property's state, and only available when a provider has been selected and can obtain the value (i.e. when running on an agent).

A Property (also in contrast to a parameter) is intended to describe a managed attribute of some system entity, such as the name or mode of a file.

The current value (is) is read and written with the methods #retrieve and #set, and the wanted value (should) is read and written with the methods #value and #value= which delegate to #should and #should=, i.e. when a property is used like any other parameter, it is the should value that is operated on.

All resource type properties in the puppet system are derived from this class.

The intention is that new parameters are created by using the DSL method Type.newproperty.

See Also:

Direct Known Subclasses

Ensure

Defined Under Namespace

Classes: Ensure

Constant Summary

Class Attribute Summary (collapse)

Attributes inherited from Parameter

#parent

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods inherited from Parameter

aliasvalue, defaultto, desc, doc, #initialize, initvars, isnamevar, isnamevar?, isrequired, munge, newvalues, nodefault, #pathbuilder, proxymethods, required?, unmunge, #unsafe_munge, validate, #validate

Methods included from Util

exit_on_fail, #exit_on_fail, which, #which

Constructor Details

This class inherits a constructor from Puppet::Parameter

Class Attribute Details

+ (Symbol) array_matching

Note:

The semantics of these modes are implemented by the method #insync?. That method is the default implementation and it has a backwards compatible behavior that imposes additional constraints on what constitutes a positive match. A derived property may override that method.

The is vs. should array matching mode; :first, or :all.

  • :first This is primarily used for single value properties. When matched against an array of values a match is true if the is value matches any of the values in the should array. When the is value is also an array, the matching is performed against the entire array as the is value.
  • :all : This is primarily used for multi-valued properties. When matched against an array of should values, the size of is and should must be the same, and all values in is must match a value in should.

See Also:

DSL:

  • type



90
91
92
# File 'lib/puppet/property.rb', line 90

def array_matching
  @array_matching ||= :first
end

Class Method Details

+ (Object) method_added(sym)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Protects against override of the #safe_insync? method.

Raises:

  • (RuntimeError)

    if the added method is :safe_insync?



297
298
299
# File 'lib/puppet/property.rb', line 297

def self.method_added(sym)
  raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync?
end

+ (Object) newvalue(name, options = {}, &block)

TODO:

Option :event original comment says "event should be returned...", is "returned" the correct word to use?

TODO:

The original documentation states that the option :method will set the name of the generated setter method, but this is not implemented. Is the documentatin or the implementation in error? (The implementation is in Puppet::Parameter::ValueCollection#new_value).

TODO:

verify that the use of :before and :after have been deprecated (or rather - never worked, and was never in use. (This means, that the option :call could be removed since calls are always :instead).

Defines a new valid value for this property. A valid value is specified as a literal (typically a Symbol), but can also be specified with a Regexp.

Options Hash (options):

  • :event (Symbol)

    The event that should be emitted when this value is set.

  • :call (Symbol)

    When to call any associated block. The default value is :instead which means that the block should be called instead of the provider. In earlier versions (before 20081031) it was possible to specify a value of :before or :after for the purpose of calling both the block and the provider. Use of these deprecated options will now raise an exception later in the process when the is value is set (see #set).

  • :invalidate_refreshes (Symbol)

    Indicates a change on this property should invalidate and remove any scheduled refreshes (from notify or subscribe) targeted at the same resource. For example, if a change in this property takes into account any changes that a scheduled refresh would have performed, then the scheduled refresh would be deleted.

  • any (Object)

    Any other option is treated as a call to a setter having the given option name (e.g. :required_features calls required_features= with the option's value as an argument).

DSL:

  • type



157
158
159
160
161
162
# File 'lib/puppet/property.rb', line 157

def self.newvalue(name, options = {}, &block)
  value = value_collection.newvalue(name, options, &block)

  define_method(value.method, &value.block) if value.method and value.block
  value
end

+ (Symbol, Regexp) value_name(name)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Looks up a value's name among valid values, to enable option lookup with result as a key.



108
109
110
111
112
# File 'lib/puppet/property.rb', line 108

def self.value_name(name)
  if value = value_collection.match?(name)
    value.name
  end
end

+ (Object) value_option(name, option)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

TODO:

Guessing on result of passing a non supported option (it performs send(option)).

Returns the value of the given option (set when a valid value with the given "name" was defined).

Raises:

  • (NoMethodError)

    if the option is not supported



122
123
124
125
126
# File 'lib/puppet/property.rb', line 122

def self.value_option(name, option)
  if value = value_collection.value(name)
    value.send(option)
  end
end

Instance Method Details

- (Object) call_provider(value)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Calls the provider setter method for this property with the given value as argument.

Raises:

  • (Puppet::Error)

    when the provider can not handle this property.

See Also:



170
171
172
173
174
175
176
# File 'lib/puppet/property.rb', line 170

def call_provider(value)
    method = self.class.name.to_s + "="
    unless provider.respond_to? method
      self.fail "The #{provider.class.name} provider can not handle attribute #{self.class.name}"
    end
    provider.send(method, value)
end

- (Object) call_valuemethod(name, value)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

TODO:

The check for a valid value option called :method does not seem to be fully supported as it seems that this option is never consulted when the method is dynamically created. Needs to be investigated. (Bug, or documentation needs to be changed).

Sets the value of this property to the given value by calling the dynamically created setter method associated with the "valid value" referenced by the given name.

Raises:

  • (Puppet::DevError)

    if there was no method to call

  • (Puppet::Error)

    if there were problems setting the value

  • (Puppet::ResourceError)

    if there was a problem setting the value and it was not raised as a Puppet::Error. The original exception is wrapped and logged.

See Also:



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/puppet/property.rb', line 191

def call_valuemethod(name, value)
  if method = self.class.value_option(name, :method) and self.respond_to?(method)
    begin
      self.send(method)
    rescue Puppet::Error
      raise
    rescue => detail
      error = Puppet::ResourceError.new("Could not set '#{value}' on #{self.class.name}: #{detail}", @resource.line, @resource.file, detail)
      error.set_backtrace detail.backtrace
      Puppet.log_exception(detail, error.message)
      raise error
    end
  elsif block = self.class.value_option(name, :block)
    # FIXME It'd be better here to define a method, so that
    # the blocks could return values.
    self.instance_eval(&block)
  else
    devfail "Could not find method for value '#{name}'"
  end
end

- (Boolean) insync?(is)

TODO:

The implementation should really do return is.zip(@should).all? {|a, b| property_matches?(a, b) } instead of using equality check and then check against an array with converted strings.

Note:

The array matching logic in this method contains backwards compatible logic that performs the comparison in :all mode by checking equality and equality of is against should converted to array of String, and that the lengths are equal, and in :first mode by checking if one of the should values is included in the is values. This means that the is value needs to be carefully arranged to match the should.

Checks if the current (is) value is in sync with the wanted (should) value. The check if the two values are in sync is controlled by the result of #match<em>all? which specifies a match of :first or :all). The matching of the _is value against the entire should value or each of the should values (as controlled by #match_all? is performed by #property_matches?.

A derived property typically only needs to override the #property_matches? method, but may also override this method if there is a need to have more control over the array matching logic.

Raises:

  • (Puppet::DevError)

    if wanted value (should) is not an array.



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/puppet/property.rb', line 321

def insync?(is)
  self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array)

  # an empty array is analogous to no should values
  return true if @should.empty?

  # Look for a matching value, either for all the @should values, or any of
  # them, depending on the configuration of this property.
  if match_all? then
    # Emulate Array#== using our own comparison function.
    # A non-array was not equal to an array, which @should always is.
    return false unless is.is_a? Array

    # If they were different lengths, they are not equal.
    return false unless is.length == @should.length

    # Finally, are all the elements equal?  In order to preserve the
    # behaviour of previous 2.7.x releases, we need to impose some fun rules
    # on "equality" here.
    #
    # Specifically, we need to implement *this* comparison: the two arrays
    # are identical if the is values are == the should values, or if the is
    # values are == the should values, stringified.
    #
    # This does mean that property equality is not commutative, and will not
    # work unless the `is` value is carefully arranged to match the should.
    return (is == @should or is == @should.map(&:to_s))

    # When we stop being idiots about this, and actually have meaningful
    # semantics, this version is the thing we actually want to do.
    #
    # return is.zip(@should).all? {|a, b| property_matches?(a, b) }
  else
    return @should.any? {|want| property_matches?(is, want) }
  end
end

- (Boolean) safe_insync?(is)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

If the wanted value (should) is not defined or is set to a non-true value then this is a state that can not be fixed and the property is reported to be in sync.

Note:

Do not override this method.

Determines whether the property is in-sync or not in a way that is protected against missing value.



285
286
287
288
289
290
291
# File 'lib/puppet/property.rb', line 285

def safe_insync?(is)
  # If there is no @should value, consider the property to be in sync.
  return true unless @should

  # Otherwise delegate to the (possibly derived) insync? method.
  insync?(is)
end

- (Object) set(value)

Note:

In older versions (before 20081031) it was possible to specify the call types :before and :after which had the effect that both the provider method and the valid value block were called. This is no longer supported.

Sets the current (is) value of this property. The value is set using the provider's setter method for this property (#call<em>provider) if nothing else has been specified. If the _valid value for the given value defines a :call option with the value :instead, the value is set with #call_valuemethod which invokes a block specified for the valid value.

Raises:

  • (Puppet::Error)

    when the provider setter should be used but there is no provider set in the associated resource

  • (Puppet::DevError)

    when a deprecated call form was specified (e.g. :before or :after).



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/puppet/property.rb', line 457

def set(value)
  # Set a name for looking up associated options like the event.
  name = self.class.value_name(value)

  call = self.class.value_option(name, :call) || :none

  if call == :instead
    call_valuemethod(name, value)
  elsif call == :none
    # They haven't provided a block, and our parent does not have
    # a provider, so we have no idea how to handle this.
    self.fail "#{self.class.name} cannot handle values of type #{value.inspect}" unless @resource.provider
    call_provider(value)
  else
    # LAK:NOTE 20081031 This is a change in behaviour -- you could
    # previously specify :call => [;before|:after], which would call
    # the setter *in addition to* the block.  I'm convinced this
    # was never used, and it makes things unecessarily complicated.
    # If you want to specify a block and still call the setter, then
    # do so in the block.
    devfail "Cannot use obsolete :call value '#{call}' for property '#{self.class.name}'"
  end
end

- (Array<Object>, ...) should

Note:

This method will potentially return different values than the original values as they are converted via munging/unmunging. If the original values are wanted, call #shouldorig.

Returns the wanted (should) value of this property. If the array matching mode #match_all? is true, an array of the wanted values in unmunged format is returned, else the first value in the array of wanted values in unmunged format is returned.

Raises:

  • (Puppet::DevError)

    if the wanted value is non nil and not an array

See Also:

  • #shouldorig


494
495
496
497
498
499
500
501
502
503
504
# File 'lib/puppet/property.rb', line 494

def should
  return nil unless defined?(@should)

  self.devfail "should for #{self.class.name} on #{resource.name} is not an array" unless @should.is_a?(Array)

  if match_all?
    return @should.collect { |val| self.unmunge(val) }
  else
    return self.unmunge(@should[0])
  end
end

- (Object) should=(values)

Sets the wanted (should) value of this property. If the given value is not already an Array, it will be wrapped in one before being set. This method also sets the cached original should values returned by #shouldorig.

Raises:



514
515
516
517
518
519
520
521
# File 'lib/puppet/property.rb', line 514

def should=(values)
  values = [values] unless values.is_a?(Array)

  @shouldorig = values

  values.each { |val| validate(val) }
  @should = values.collect { |val| self.munge(val) }
end

- unsafe_validate(value)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Asserts that the given value is valid. If the developer uses a 'validate' hook, this method will get overridden.

Raises:

  • (Exception)

    if the value is invalid, or value can not be handled.



546
547
548
549
# File 'lib/puppet/property.rb', line 546

def unsafe_validate(value)
  super
  validate_features_per_value(value)
end

- validate_features_per_value(value)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Asserts that all required provider features are present for the given property value.

Raises:

  • (ArgumentError)

    if a required feature is not present



556
557
558
559
560
561
562
# File 'lib/puppet/property.rb', line 556

def validate_features_per_value(value)
  if features = self.class.value_option(self.class.value_name(value), :required_features)
    features = Array(features)
    needed_features = features.collect { |f| f.to_s }.join(", ")
    raise ArgumentError, "Provider must have features '#{needed_features}' to set '#{self.class.name}' to '#{value}'" unless provider.satisfies?(features)
  end
end

- (Object) value=(values)

Sets the wanted (should) value of this property. If the given value is not already an Array, it will be wrapped in one before being set. This method also sets the cached original should values returned by #shouldorig.

Raises:



570
571
572
# File 'lib/puppet/property.rb', line 570

def value=(values)
  self.should = values
end