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
|
require 'puppet/resource/status'
class Puppet::Transaction::ResourceHarness
extend Forwardable
def_delegators :@transaction, :relationship_graph
attr_reader :transaction
def allow_changes?(resource)
if resource.purging? and resource.deleting? and 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
else
true
end
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 perform_changes(resource)
current = resource.retrieve_resource
cache resource, :checked, Time.now
return [] if ! allow_changes?(resource)
current_values = current.to_hash
historical_values = Puppet::Util::Storage.cache(resource).dup
desired_values = resource.to_resource.to_hash
audited_params = (resource[:audit] || []).map { |p| p.to_sym }
synced_params = []
# Record the current state in state.yml.
audited_params.each do |param|
cache(resource, param, current_values[param])
end
# Update the machine state & create logs/events
events = []
ensure_param = resource.parameter(:ensure)
if desired_values[:ensure] && !ensure_param.insync?(current_values[:ensure])
events << apply_parameter(ensure_param, current_values[:ensure], audited_params.include?(:ensure), historical_values[:ensure])
synced_params << :ensure
elsif current_values[:ensure] != :absent
work_order = resource.properties # Note: only the resource knows what order to apply changes in
work_order.each do |param|
if !param.insync?(current_values[param.name])
events << apply_parameter(param, current_values[param.name], audited_params.include?(param.name), historical_values[param.name])
synced_params << param.name
end
end
end
# Add more events to capture audit results
audited_params.each do |param_name|
if historical_values.include?(param_name)
if historical_values[param_name] != current_values[param_name] && !synced_params.include?(param_name)
event = create_change_event(resource.parameter(param_name), current_values[param_name], true, historical_values[param_name])
event.send_log
events << event
end
else
resource.property(param_name).notice "audit change: newly-recorded value #{current_values[param_name]}"
end
end
events
end
def create_change_event(property, current_value, do_audit, historical_value)
event = property.event
event.previous_value = current_value
event.desired_value = property.should
event.historical_value = historical_value
if do_audit
event.audited = true
event.status = "audit"
if historical_value != current_value
event.message = "audit change: previously recorded value #{property.is_to_s(historical_value)} has been changed to #{property.is_to_s(current_value)}"
end
end
event
end
def apply_parameter(property, current_value, do_audit, historical_value)
event = create_change_event(property, current_value, do_audit, historical_value)
if do_audit && historical_value && historical_value != current_value
brief_audit_message = " (previously recorded value was #{property.is_to_s(historical_value)})"
else
brief_audit_message = ""
end
if property.noop
event.message = "current_value #{property.is_to_s(current_value)}, should be #{property.should_to_s(property.should)} (noop)#{brief_audit_message}"
event.status = "noop"
else
property.sync
event.message = [ property.change_to_s(current_value, property.should), brief_audit_message ].join
event.status = "success"
end
event
rescue => detail
puts detail.backtrace if Puppet[:trace]
event.status = "failure"
event.message = "change from #{property.is_to_s(current_value)} to #{property.should_to_s(property.should)} failed: #{detail}"
event
ensure
event.send_log
end
def evaluate(resource)
start = Time.now
status = Puppet::Resource::Status.new(resource)
perform_changes(resource).each do |event|
status << event
end
if status.changed? && ! resource.noop?
cache(resource, :synced, Time.now)
resource.flush if resource.respond_to?(:flush)
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
|