1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
|
# The virtual base class for properties, which are the self-contained building
# blocks for actually doing work on the system.
require 'puppet'
require 'puppet/propertychange'
require 'puppet/parameter'
module Puppet
class Property < Puppet::Parameter
# Because 'should' uses an array, we have a special method for handling
# it. We also want to keep copies of the original values, so that
# they can be retrieved and compared later when merging.
attr_reader :shouldorig
attr_writer :noop
class << self
attr_accessor :unmanaged
attr_reader :name
# Return array matching info, defaulting to just matching
# the first value.
def array_matching
unless defined?(@array_matching)
@array_matching = :first
end
@array_matching
end
# Set whether properties should match all values or just the first one.
def array_matching=(value)
value = value.intern if value.is_a?(String)
unless [:first, :all].include?(value)
raise ArgumentError, "Supported values for Property#array_matching are 'first' and 'all'"
end
@array_matching = value
end
def checkable
@checkable = true
end
def uncheckable
@checkable = false
end
def checkable?
if defined? @checkable
return @checkable
else
return true
end
end
end
# Look up a value's name, so we can find options and such.
def self.value_name(value)
name = symbolize(value)
if @parametervalues[name]
return name
elsif ary = self.match?(value)
return ary[0]
else
return nil
end
end
# Retrieve an option set when a value was defined.
def self.value_option(name, option)
if option.is_a?(String)
option = symbolize(option)
end
if hash = @parameteroptions[name]
hash[option]
else
nil
end
end
# Create the value management variables.
def self.initvars
@parametervalues = {}
@aliasvalues = {}
@parameterregexes = {}
@parameteroptions = {}
end
# Define a new valid value for a property. You must provide the value itself,
# usually as a symbol, or a regex to match the value.
#
# The first argument to the method is either the value itself or a regex.
# The second argument is an option hash; valid options are:
# * <tt>:event</tt>: The event that should be returned when this value is set.
# * <tt>:call</tt>: When to call any associated block. The default value
# is ``instead``, which means to call the value instead of calling the
# provider. You can also specify ``before`` or ``after``, which will
# call both the block and the provider, according to the order you specify
# (the ``first`` refers to when the block is called, not the provider).
def self.newvalue(name, options = {}, &block)
name = name.intern if name.is_a? String
@parameteroptions[name] = {}
paramopts = @parameteroptions[name]
# Symbolize everything
options.each do |opt, val|
paramopts[symbolize(opt)] = symbolize(val)
end
# By default, call the block instead of the provider.
if block_given?
paramopts[:call] ||= :instead
else
paramopts[:call] ||= :none
end
# If there was no block given, we still want to store the information
# for validation, but we won't be defining a method
block ||= true
case name
when Symbol
if @parametervalues.include?(name)
Puppet.warning "%s reassigning value %s" % [self.name, name]
end
@parametervalues[name] = block
if block_given?
method = "set_" + name.to_s
settor = paramopts[:settor] || (self.name.to_s + "=")
define_method(method, &block)
paramopts[:method] = method
end
when Regexp
# The regexes are handled in parameter.rb. This value is used
# for validation.
@parameterregexes[name] = block
# This is used for looking up the block for execution.
if block_given?
paramopts[:block] = block
end
else
raise ArgumentError, "Invalid value %s of type %s" %
[name, name.class]
end
end
# Call the provider method.
def call_provider(value)
begin
provider.send(self.class.name.to_s + "=", value)
rescue NoMethodError
self.fail "The %s provider can not handle attribute %s" %
[provider.class.name, self.class.name]
end
end
# Call the dynamically-created method associated with our value, if
# there is one.
def call_valuemethod(name, value)
event = nil
if method = self.class.value_option(name, :method) and self.respond_to?(method)
self.debug "setting %s (currently %s)" % [value, self.retrieve]
begin
event = self.send(method)
rescue Puppet::Error
raise
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
error = Puppet::Error.new("Could not set %s on %s: %s" %
[value, self.class.name, detail], @resource.line, @resource.file)
error.set_backtrace detail.backtrace
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.
# If the regex was defined with no associated block, then just pass
# through and the correct event will be passed back.
event = self.instance_eval(&block)
end
return event, name
end
# How should a property change be printed as a string?
def change_to_s(currentvalue, newvalue)
begin
if currentvalue == :absent
return "defined '%s' as '%s'" %
[self.name, self.should_to_s(newvalue)]
elsif newvalue == :absent or newvalue == [:absent]
return "undefined %s from '%s'" %
[self.name, self.is_to_s(currentvalue)]
else
return "%s changed '%s' to '%s'" %
[self.name, self.is_to_s(currentvalue), self.should_to_s(newvalue)]
end
rescue Puppet::Error, Puppet::DevError
raise
rescue => detail
raise Puppet::DevError, "Could not convert change %s to string: %s" %
[self.name, detail]
end
end
# Figure out which event to return.
def event(name, event = nil)
if value_event = self.class.value_option(name, :event)
return value_event
else
if event and event.is_a?(Symbol)
if event == :nochange
return nil
else
return event
end
else
if self.class.name == :ensure
event = case self.should
when :present: (@resource.class.name.to_s + "_created").intern
when :absent: (@resource.class.name.to_s + "_removed").intern
else
(@resource.class.name.to_s + "_changed").intern
end
else
event = (@resource.class.name.to_s + "_changed").intern
end
end
end
return event
end
# initialize our property
def initialize(hash = {})
super
end
def inspect
str = "Property('%s', " % self.name
if defined? @should and @should
str += "@should = '%s')" % @should.join(", ")
else
str += "@should = nil)"
end
end
# Determine whether the property is in-sync or not. If @should is
# not defined or is set to a non-true value, then we do not have
# a valid value for it and thus consider the property to be in-sync
# since we cannot fix it. Otherwise, we expect our should value
# to be an array, and if @is matches any of those values, then
# we consider it to be in-sync.
def insync?(is)
#debug "%s value is '%s', should be '%s'" %
# [self,self.is.inspect,self.should.inspect]
unless defined? @should and @should
return true
end
unless @should.is_a?(Array)
self.devfail "%s's should is not array" % self.class.name
end
# an empty array is analogous to no should values
if @should.empty?
return true
end
# Look for a matching value
if match_all?
return (is == @should or is == @should.collect { |v| v.to_s })
else
@should.each { |val|
if is == val or is == val.to_s
return true
end
}
end
# otherwise, return false
return false
end
# because the @should and @is vars might be in weird formats,
# we need to set up a mechanism for pretty printing of the values
# default to just the values, but this way individual properties can
# override these methods
def is_to_s(currentvalue)
currentvalue
end
# Send a log message.
def log(msg)
unless @resource[:loglevel]
self.devfail "Parent %s has no loglevel" %
@resource.name
end
Puppet::Util::Log.create(
:level => @resource[:loglevel],
:message => msg,
:source => self
)
end
# Should we match all values, or just the first?
def match_all?
self.class.array_matching == :all
end
# each property class must define the name() method, and property instances
# do not change that name
# this implicitly means that a given object can only have one property
# instance of a given property class
def name
return self.class.name
end
# for testing whether we should actually do anything
def noop
# This is only here to make testing easier.
if @resource.respond_to?(:noop?)
@resource.noop?
else
if defined?(@noop)
@noop
else
Puppet[:noop]
end
end
end
# By default, call the method associated with the property name on our
# provider. In other words, if the property name is 'gid', we'll call
# 'provider.gid' to retrieve the current value.
def retrieve
is = provider.send(self.class.name)
# puts "IS is: " + is.to_s
# puts "and its an array!!!" if is.is_a? Array
return is
end
# Set our value, using the provider, an associated block, or both.
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)
# If we're supposed to call the block first or instead, call it now
if call == :before or call == :instead
event, tmp = call_valuemethod(name, value)
end
unless call == :instead
if @resource.provider
call_provider(value)
else
# 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 "%s cannot handle values of type %s" %
[self.class.name, value.inspect]
end
end
if call == :after
event, tmp = call_valuemethod(name, value)
end
return event(name, event)
end
# Only return the first value
def should
if defined? @should
unless @should.is_a?(Array)
self.devfail "should for %s on %s is not an array" %
[self.class.name, @resource.name]
end
if match_all?
return @should
else
return @should[0]
end
else
return nil
end
end
# Set the should value.
def should=(values)
unless values.is_a?(Array)
values = [values]
end
@shouldorig = values
if self.respond_to?(:validate)
values.each { |val|
validate(val)
}
end
if self.respond_to?(:munge)
@should = values.collect { |val|
self.munge(val)
}
else
@should = values
end
end
def should_to_s(newvalue)
newvalue = [newvalue] unless newvalue.is_a? Array
if defined? newvalue
newvalue.join(" ")
else
return nil
end
end
# The default 'sync' method only selects among a list of registered
# values.
def sync
# if self.insync?
# self.info "already in sync"
# return nil
# end
unless self.class.values
self.devfail "No values defined for %s" %
self.class.name
end
if value = self.should
set(value)
else
self.devfail "Got a nil value for should"
end
end
# The properties need to return tags so that logs correctly collect them.
def tags
unless defined? @tags
@tags = []
# This might not be true in testing
if @resource.respond_to? :tags
@tags = @resource.tags
end
@tags << self.name
end
@tags
end
def to_s
return "%s(%s)" % [@resource.name,self.name]
end
# Provide a common hook for setting @should, just like params.
def value=(value)
self.should = value
end
# This property will get automatically added to any type that responds
# to the methods 'exists?', 'create', and 'destroy'.
class Ensure < Puppet::Property
@name = :ensure
def self.defaultvalues
newvalue(:present) do
if @resource.provider and @resource.provider.respond_to?(:create)
@resource.provider.create
else
@resource.create
end
nil # return nil so the event is autogenerated
end
newvalue(:absent) do
if @resource.provider and @resource.provider.respond_to?(:destroy)
@resource.provider.destroy
else
@resource.destroy
end
nil # return nil so the event is autogenerated
end
defaultto do
if @resource.managed?
:present
else
nil
end
end
# This doc will probably get overridden
@doc ||= "The basic property that the object should be in."
end
def self.inherited(sub)
# Add in the two properties that everyone will have.
sub.class_eval do
end
end
def change_to_s(currentvalue, newvalue)
begin
if currentvalue == :absent or currentvalue.nil?
return "created"
elsif newvalue == :absent
return "removed"
else
return "%s changed '%s' to '%s'" %
[self.name, self.is_to_s(currentvalue), self.should_to_s(newvalue)]
end
rescue Puppet::Error, Puppet::DevError
raise
rescue => detail
raise Puppet::DevError, "Could not convert change %s to string: %s" %
[self.name, detail]
end
end
def retrieve
# XXX This is a problem -- whether the object exists or not often
# depends on the results of other properties, yet we're the first property
# to get checked, which means that those other properties do not have
# @is values set. This seems to be the source of quite a few bugs,
# although they're mostly logging bugs, not functional ones.
if prov = @resource.provider and prov.respond_to?(:exists?)
result = prov.exists?
elsif @resource.respond_to?(:exists?)
result = @resource.exists?
else
raise Puppet::DevError, "No ability to determine if %s exists" %
@resource.class.name
end
if result
return :present
else
return :absent
end
end
# If they're talking about the thing at all, they generally want to
# say it should exist.
#defaultto :present
defaultto do
if @resource.managed?
:present
else
nil
end
end
end
end
end
# $Id$
|