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
|
require 'puppet/resource/status'
class Puppet::Transaction::ResourceHarness
extend Forwardable
def_delegators :@transaction, :relationship_graph
attr_reader :transaction
def allow_changes?(resource)
return true unless resource.purging? and resource.deleting?
return true unless deps = relationship_graph.dependents(resource) and ! deps.empty? and deps.detect { |d| ! d.deleting? }
deplabel = deps.collect { |r| r.ref }.join(",")
plurality = deps.length > 1 ? "":"s"
resource.warning "#{deplabel} still depend#{plurality} on me -- not purging"
false
end
def apply_changes(status, changes)
changes.each do |change|
status << change.apply
cache(change.property.resource, change.property.name, change.is) if change.auditing?
end
status.changed = true
end
# Used mostly for scheduling and auditing at this point.
def cached(resource, name)
Puppet::Util::Storage.cache(resource)[name]
end
# Used mostly for scheduling and auditing at this point.
def cache(resource, name, value)
Puppet::Util::Storage.cache(resource)[name] = value
end
def changes_to_perform(status, resource)
current = resource.retrieve_resource
cache resource, :checked, Time.now
return [] if ! allow_changes?(resource)
audited = copy_audited_parameters(resource, current)
if param = resource.parameter(:ensure)
return [] if absent_and_not_being_created?(current, param)
unless ensure_is_insync?(current, param)
audited.keys.reject{|name| name == :ensure}.each do |name|
resource.parameter(name).notice "audit change: previously recorded value #{audited[name]} has been changed to #{current[param]}"
cache(resource, name, current[param])
end
return [Puppet::Transaction::Change.new(param, current[:ensure])]
end
return [] if ensure_should_be_absent?(current, param)
end
resource.properties.reject { |param| param.name == :ensure }.select do |param|
(audited.include?(param.name) && audited[param.name] != current[param.name]) || (param.should != nil && !param_is_insync?(current, param))
end.collect do |param|
change = Puppet::Transaction::Change.new(param, current[param.name])
change.auditing = true if audited.include?(param.name)
change.old_audit_value = audited[param.name]
change
end
end
def copy_audited_parameters(resource, current)
return {} unless audit = resource[:audit]
audit = Array(audit).collect { |p| p.to_sym }
audited = {}
audit.find_all do |param|
if value = cached(resource, param)
audited[param] = value
else
resource.property(param).notice "audit change: newly-recorded recorded value #{current[param]}"
cache(resource, param, current[param])
end
end
audited
end
def evaluate(resource)
start = Time.now
status = Puppet::Resource::Status.new(resource)
if changes = changes_to_perform(status, resource) and ! changes.empty?
status.out_of_sync = true
status.change_count = changes.length
apply_changes(status, changes)
if ! resource.noop?
cache(resource, :synced, Time.now)
resource.flush if resource.respond_to?(:flush)
end
end
return status
rescue => detail
resource.fail "Could not create resource status: #{detail}" unless status
puts detail.backtrace if Puppet[:trace]
resource.err "Could not evaluate: #{detail}"
status.failed = true
return status
ensure
(status.evaluation_time = Time.now - start) if status
end
def initialize(transaction)
@transaction = transaction
end
def scheduled?(status, resource)
return true if Puppet[:ignoreschedules]
return true unless schedule = schedule(resource)
# We use 'checked' here instead of 'synced' because otherwise we'll
# end up checking most resources most times, because they will generally
# have been synced a long time ago (e.g., a file only gets updated
# once a month on the server and its schedule is daily; the last sync time
# will have been a month ago, so we'd end up checking every run).
schedule.match?(cached(resource, :checked).to_i)
end
def schedule(resource)
unless resource.catalog
resource.warning "Cannot schedule without a schedule-containing catalog"
return nil
end
return nil unless name = resource[:schedule]
resource.catalog.resource(:schedule, name) || resource.fail("Could not find schedule #{name}")
end
private
def absent_and_not_being_created?(current, param)
current[:ensure] == :absent and param.should.nil?
end
def ensure_is_insync?(current, param)
param.insync?(current[:ensure])
end
def ensure_should_be_absent?(current, param)
param.should == :absent
end
def param_is_insync?(current, param)
param.insync?(current[param.name])
end
end
|