1
# The virtual base class for properties, which are the self-contained building
2
# blocks for actually doing work on the system.
5
require 'puppet/parameter'
8
class Property < Puppet::Parameter
10
# Because 'should' uses an array, we have a special method for handling
11
# it. We also want to keep copies of the original values, so that
12
# they can be retrieved and compared later when merging.
13
attr_reader :shouldorig
18
attr_accessor :unmanaged
21
# Return array matching info, defaulting to just matching
24
unless defined?(@array_matching)
25
@array_matching = :first
30
# Set whether properties should match all values or just the first one.
31
def array_matching=(value)
32
value = value.intern if value.is_a?(String)
33
unless [:first, :all].include?(value)
34
raise ArgumentError, "Supported values for Property#array_matching are 'first' and 'all'"
36
@array_matching = value
48
if defined? @checkable
56
# Look up a value's name, so we can find options and such.
57
def self.value_name(value)
58
if value != '' and name = symbolize(value) and @parametervalues.include?(name)
60
elsif ary = self.match?(value)
67
# Retrieve an option set when a value was defined.
68
def self.value_option(name, option)
69
option = option.to_sym
70
if hash = @parameteroptions[name]
77
# Create the value management variables.
81
@parameterregexes = {}
82
@parameteroptions = {}
85
# Define a new valid value for a property. You must provide the value itself,
86
# usually as a symbol, or a regex to match the value.
88
# The first argument to the method is either the value itself or a regex.
89
# The second argument is an option hash; valid options are:
90
# * <tt>:event</tt>: The event that should be returned when this value is set.
91
# * <tt>:call</tt>: When to call any associated block. The default value
92
# is ``instead``, which means to call the value instead of calling the
93
# provider. You can also specify ``before`` or ``after``, which will
94
# call both the block and the provider, according to the order you specify
95
# (the ``first`` refers to when the block is called, not the provider).
96
def self.newvalue(name, options = {}, &block)
97
name = name.intern if name.is_a? String
99
@parameteroptions[name] = {}
100
paramopts = @parameteroptions[name]
102
# Symbolize everything
103
options.each do |opt, val|
104
paramopts[symbolize(opt)] = symbolize(val)
107
# By default, call the block instead of the provider.
109
paramopts[:call] ||= :instead
111
paramopts[:call] ||= :none
113
# If there was no block given, we still want to store the information
114
# for validation, but we won't be defining a method
119
if @parametervalues.include?(name)
120
Puppet.warning "%s reassigning value %s" % [self.name, name]
122
@parametervalues[name] = block
125
method = "set_" + name.to_s
126
settor = paramopts[:settor] || (self.name.to_s + "=")
127
define_method(method, &block)
128
paramopts[:method] = method
131
# The regexes are handled in parameter.rb. This value is used
133
@parameterregexes[name] = block
135
# This is used for looking up the block for execution.
137
paramopts[:block] = block
140
raise ArgumentError, "Invalid value %s of type %s" %
145
# Call the provider method.
146
def call_provider(value)
148
provider.send(self.class.name.to_s + "=", value)
150
self.fail "The %s provider can not handle attribute %s" %
151
[provider.class.name, self.class.name]
155
# Call the dynamically-created method associated with our value, if
157
def call_valuemethod(name, value)
159
if method = self.class.value_option(name, :method) and self.respond_to?(method)
160
#self.debug "setting %s (currently %s)" % [value, self.retrieve]
163
event = self.send(method)
168
puts detail.backtrace
170
error = Puppet::Error.new("Could not set %s on %s: %s" %
171
[value, self.class.name, detail], @resource.line, @resource.file)
172
error.set_backtrace detail.backtrace
175
elsif block = self.class.value_option(name, :block)
176
# FIXME It'd be better here to define a method, so that
177
# the blocks could return values.
178
# If the regex was defined with no associated block, then just pass
179
# through and the correct event will be passed back.
180
event = self.instance_eval(&block)
185
# How should a property change be printed as a string?
186
def change_to_s(currentvalue, newvalue)
188
if currentvalue == :absent
189
return "defined '%s' as '%s'" %
190
[self.name, self.should_to_s(newvalue)]
191
elsif newvalue == :absent or newvalue == [:absent]
192
return "undefined %s from '%s'" %
193
[self.name, self.is_to_s(currentvalue)]
195
return "%s changed '%s' to '%s'" %
196
[self.name, self.is_to_s(currentvalue), self.should_to_s(newvalue)]
198
rescue Puppet::Error, Puppet::DevError
201
raise Puppet::DevError, "Could not convert change %s to string: %s" %
206
# Figure out which event to return.
207
def event(name, event = nil)
208
if value_event = self.class.value_option(name, :event)
212
if event and event.is_a?(Symbol)
213
if event == :nochange
220
if self.class.name == :ensure
221
event = case self.should
222
when :present: (@resource.class.name.to_s + "_created").intern
223
when :absent: (@resource.class.name.to_s + "_removed").intern
225
(@resource.class.name.to_s + "_changed").intern
228
event = (@resource.class.name.to_s + "_changed").intern
234
# initialize our property
235
def initialize(hash = {})
240
str = "Property('%s', " % self.name
242
if defined? @should and @should
243
str += "@should = '%s')" % @should.join(", ")
245
str += "@should = nil)"
249
# Determine whether the property is in-sync or not. If @should is
250
# not defined or is set to a non-true value, then we do not have
251
# a valid value for it and thus consider the property to be in-sync
252
# since we cannot fix it. Otherwise, we expect our should value
253
# to be an array, and if @is matches any of those values, then
254
# we consider it to be in-sync.
256
#debug "%s value is '%s', should be '%s'" %
257
# [self,self.is.inspect,self.should.inspect]
258
unless defined? @should and @should
262
unless @should.is_a?(Array)
263
self.devfail "%s's should is not array" % self.class.name
266
# an empty array is analogous to no should values
271
# Look for a matching value
273
return (is == @should or is == @should.collect { |v| v.to_s })
276
if is == val or is == val.to_s
282
# otherwise, return false
286
# because the @should and @is vars might be in weird formats,
287
# we need to set up a mechanism for pretty printing of the values
288
# default to just the values, but this way individual properties can
289
# override these methods
290
def is_to_s(currentvalue)
294
# Send a log message.
296
unless @resource[:loglevel]
297
self.devfail "Parent %s has no loglevel" % @resource.name
299
Puppet::Util::Log.create(
300
:level => @resource[:loglevel],
306
# Should we match all values, or just the first?
308
self.class.array_matching == :all
311
# each property class must define the name() method, and property instances
312
# do not change that name
313
# this implicitly means that a given object can only have one property
314
# instance of a given property class
316
return self.class.name
319
# for testing whether we should actually do anything
321
# This is only here to make testing easier.
322
if @resource.respond_to?(:noop?)
333
# By default, call the method associated with the property name on our
334
# provider. In other words, if the property name is 'gid', we'll call
335
# 'provider.gid' to retrieve the current value.
337
provider.send(self.class.name)
340
# Set our value, using the provider, an associated block, or both.
342
# Set a name for looking up associated options like the event.
343
name = self.class.value_name(value)
345
call = self.class.value_option(name, :call)
347
# If we're supposed to call the block first or instead, call it now
348
if call == :before or call == :instead
349
event, tmp = call_valuemethod(name, value)
351
unless call == :instead
352
if @resource.provider
355
# They haven't provided a block, and our parent does not have
356
# a provider, so we have no idea how to handle this.
357
self.fail "%s cannot handle values of type %s" %
358
[self.class.name, value.inspect]
362
event, tmp = call_valuemethod(name, value)
365
return event(name, event)
368
# Only return the first value
371
unless @should.is_a?(Array)
372
self.devfail "should for %s on %s is not an array" %
373
[self.class.name, @resource.name]
385
# Set the should value.
387
unless values.is_a?(Array)
393
if self.respond_to?(:validate)
398
if self.respond_to?(:munge)
399
@should = values.collect { |val|
407
def should_to_s(newvalue)
408
newvalue = [newvalue] unless newvalue.is_a? Array
416
# The default 'sync' method only selects among a list of registered # values.
418
self.devfail("No values defined for %s" % self.class.name) unless self.class.values
420
if value = self.should
423
self.devfail "Got a nil value for should"
427
# The properties need to return tags so that logs correctly collect them.
429
unless defined? @tags
431
# This might not be true in testing
432
if @resource.respond_to? :tags
433
@tags = @resource.tags
441
return "%s(%s)" % [@resource.name,self.name]
444
# Provide a common hook for setting @should, just like params.
449
# This property will get automatically added to any type that responds
450
# to the methods 'exists?', 'create', and 'destroy'.
451
class Ensure < Puppet::Property
454
def self.defaultvalues
455
newvalue(:present) do
456
if @resource.provider and @resource.provider.respond_to?(:create)
457
@resource.provider.create
461
nil # return nil so the event is autogenerated
465
if @resource.provider and @resource.provider.respond_to?(:destroy)
466
@resource.provider.destroy
470
nil # return nil so the event is autogenerated
474
if @resource.managed?
481
# This doc will probably get overridden
482
@doc ||= "The basic property that the object should be in."
485
def self.inherited(sub)
486
# Add in the two properties that everyone will have.
491
def change_to_s(currentvalue, newvalue)
493
if currentvalue == :absent or currentvalue.nil?
495
elsif newvalue == :absent
498
return "%s changed '%s' to '%s'" %
499
[self.name, self.is_to_s(currentvalue), self.should_to_s(newvalue)]
501
rescue Puppet::Error, Puppet::DevError
504
raise Puppet::DevError, "Could not convert change %s to string: %s" %
510
# XXX This is a problem -- whether the object exists or not often
511
# depends on the results of other properties, yet we're the first property
512
# to get checked, which means that those other properties do not have
513
# @is values set. This seems to be the source of quite a few bugs,
514
# although they're mostly logging bugs, not functional ones.
515
if prov = @resource.provider and prov.respond_to?(:exists?)
516
result = prov.exists?
517
elsif @resource.respond_to?(:exists?)
518
result = @resource.exists?
520
raise Puppet::DevError, "No ability to determine if %s exists" %
530
# If they're talking about the thing at all, they generally want to
531
# say it should exist.
534
if @resource.managed?