Class: Puppet::Transaction

Inherits:
Object show all
Includes:
Util, Util::Tagging
Defined in:
lib/puppet/transaction.rb

Defined Under Namespace

Classes: Event, EventManager, Report, ResourceHarness

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util::Tagging

#tag, #tagged?, #tags=

Methods included from Util

activerecord_version, benchmark, chuser, classproxy, #execfail, #execpipe, execute, logmethods, memory, proxy, recmkdir, secure_open, symbolize, symbolizehash, symbolizehash!, synchronize_on, thinmark, #threadlock, which, withumask

Methods included from Util::POSIX

#get_posix_field, #gid, #idfield, #methodbyid, #methodbyname, #search_posix_field, #uid

Constructor Details

#initialize(catalog, report = nil) ⇒ Transaction

this should only be called by a Puppet::Type::Component resource now and it should only receive an array



223
224
225
226
227
228
# File 'lib/puppet/transaction.rb', line 223

def initialize(catalog, report = nil)
  @catalog = catalog
  @report = report || Report.new("apply", catalog.version)
  @event_manager = Puppet::Transaction::EventManager.new(self)
  @resource_harness = Puppet::Transaction::ResourceHarness.new(self)
end

Instance Attribute Details

#catalogObject

Returns the value of attribute catalog.



14
15
16
# File 'lib/puppet/transaction.rb', line 14

def catalog
  @catalog
end

#componentObject

Returns the value of attribute component.



14
15
16
# File 'lib/puppet/transaction.rb', line 14

def component
  @component
end

#configuratorObject

Returns the value of attribute configurator.



15
16
17
# File 'lib/puppet/transaction.rb', line 15

def configurator
  @configurator
end

#event_managerObject (readonly)

Routes and stores any events and subscriptions.



21
22
23
# File 'lib/puppet/transaction.rb', line 21

def event_manager
  @event_manager
end

#ignoreschedulesObject

Returns the value of attribute ignoreschedules.



14
15
16
# File 'lib/puppet/transaction.rb', line 14

def ignoreschedules
  @ignoreschedules
end

#reportObject (readonly)

The report, once generated.



18
19
20
# File 'lib/puppet/transaction.rb', line 18

def report
  @report
end

#resource_harnessObject (readonly)

Handles most of the actual interacting with resources



24
25
26
# File 'lib/puppet/transaction.rb', line 24

def resource_harness
  @resource_harness
end

#sorted_resourcesObject

Returns the value of attribute sorted_resources.



15
16
17
# File 'lib/puppet/transaction.rb', line 15

def sorted_resources
  @sorted_resources
end

Instance Method Details

#add_resource_status(status) ⇒ Object



270
271
272
# File 'lib/puppet/transaction.rb', line 270

def add_resource_status(status)
  report.add_resource_status status
end

#add_times(hash) ⇒ Object

Add some additional times for reporting



35
36
37
38
39
# File 'lib/puppet/transaction.rb', line 35

def add_times(hash)
  hash.each do |name, num|
    report.add_times(name, num)
  end
end

#any_failed?Boolean

Are there any failed resources in this transaction?

Returns:

  • (Boolean)


42
43
44
# File 'lib/puppet/transaction.rb', line 42

def any_failed?
  report.resource_statuses.values.detect { |status| status.failed? }
end

#apply(resource, ancestor = nil) ⇒ Object

Apply all changes for a resource



47
48
49
50
51
52
53
# File 'lib/puppet/transaction.rb', line 47

def apply(resource, ancestor = nil)
  status = resource_harness.evaluate(resource)
  add_resource_status(status)
  event_manager.queue_events(ancestor || resource, status.events) unless status.failed?
rescue => detail
  resource.err "Could not evaluate: #{detail}"
end

#changed?Boolean

Find all of the changed resources.

Returns:

  • (Boolean)


56
57
58
# File 'lib/puppet/transaction.rb', line 56

def changed?
  report.resource_statuses.values.find_all { |status| status.changed }.collect { |status| catalog.resource(status.resource) }
end

#eval_children_and_apply_resource(resource, ancestor = nil) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/puppet/transaction.rb', line 98

def eval_children_and_apply_resource(resource, ancestor = nil)
  resource_status(resource).scheduled = true

  # We need to generate first regardless, because the recursive
  # actions sometimes change how the top resource is applied.
  children = eval_generate(resource)

  if ! children.empty? and resource.depthfirst?
    children.each do |child|
      # The child will never be skipped when the parent isn't
      eval_resource(child, ancestor || resource)
    end
  end

  # Perform the actual changes
  apply(resource, ancestor)

  if ! children.empty? and ! resource.depthfirst?
    children.each do |child|
      eval_resource(child, ancestor || resource)
    end
  end
end

#eval_generate(resource) ⇒ Object

See if the resource generates new resources at evaluation time.



82
83
84
# File 'lib/puppet/transaction.rb', line 82

def eval_generate(resource)
  generate_additional_resources(resource, :eval_generate)
end

#eval_resource(resource, ancestor = nil) ⇒ Object

Evaluate a single resource.



87
88
89
90
91
92
93
94
95
96
# File 'lib/puppet/transaction.rb', line 87

def eval_resource(resource, ancestor = nil)
  if skip?(resource)
    resource_status(resource).skipped = true
  else
    eval_children_and_apply_resource(resource, ancestor)
  end

  # Check to see if there are any events queued for this resource
  event_manager.process_events(resource)
end

#evaluateObject

This method does all the actual work of running a transaction. It collects all of the changes, executes them, and responds to any necessary events.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/puppet/transaction.rb', line 125

def evaluate
  prepare

  Puppet.info "Applying configuration version '#{catalog.version}'" if catalog.version

  @sorted_resources.each do |resource|
    next if stop_processing?
    if resource.is_a?(Puppet::Type::Component)
      Puppet.warning "Somehow left a component in the relationship graph"
      next
    end
    ret = nil
    seconds = thinmark do
      ret = eval_resource(resource)
    end

    resource.info "valuated in %0.2f seconds" % seconds if Puppet[:evaltrace] and @catalog.host_config?
    ret
  end

  Puppet.debug "Finishing transaction #{object_id}"
end

#eventsObject



148
149
150
# File 'lib/puppet/transaction.rb', line 148

def events
  event_manager.events
end

#failed?(resource) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/puppet/transaction.rb', line 152

def failed?(resource)
  s = resource_status(resource) and s.failed?
end

#failed_dependencies?(resource) ⇒ Boolean

Does this resource have any failed dependencies?

Returns:

  • (Boolean)


157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/puppet/transaction.rb', line 157

def failed_dependencies?(resource)
  # First make sure there are no failed dependencies.  To do this,
  # we check for failures in any of the vertexes above us.  It's not
  # enough to check the immediate dependencies, which is why we use
  # a tree from the reversed graph.
  found_failed = false
  relationship_graph.dependencies(resource).each do |dep|
    next unless failed?(dep)
    resource.notice "Dependency #{dep} has failures: #{resource_status(dep).failed}"
    found_failed = true
  end

  found_failed
end

#generateObject

Collect any dynamically generated resources. This method is called before the transaction starts.



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/puppet/transaction.rb', line 204

def generate
  list = @catalog.vertices
  newlist = []
  while ! list.empty?
    list.each do |resource|
      newlist += generate_additional_resources(resource, :generate)
    end
    list = newlist
    newlist = []
  end
end

#generate_additional_resources(resource, method) ⇒ Object

A general method for recursively generating new resources from a resource.



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
# File 'lib/puppet/transaction.rb', line 174

def generate_additional_resources(resource, method)
  return [] unless resource.respond_to?(method)
  begin
    made = resource.send(method)
  rescue => detail
    puts detail.backtrace if Puppet[:trace]
    resource.err "Failed to generate additional resources using '#{method}': #{detail}"
  end
  return [] unless made
  made = [made] unless made.is_a?(Array)
  made.uniq.find_all do |res|
    begin
      res.tag(*resource.tags)
      @catalog.add_resource(res) do |r|
        r.finish
        make_parent_child_relationship(resource, [r])

        # Call 'generate' recursively
        generate_additional_resources(r, method)
      end
      true
    rescue Puppet::Resource::Catalog::DuplicateResourceError
      res.info "Duplicate generated resource; skipping"
      false
    end
  end
end

#handle_qualified_tags(qualified) ⇒ Object



306
307
308
309
310
311
# File 'lib/puppet/transaction.rb', line 306

def handle_qualified_tags( qualified )
  # The default behavior of Puppet::Util::Tagging is
  # to split qualified tags into parts. That would cause
  # qualified tags to match too broadly here.
  return
end

#ignore_tags?Boolean

Should we ignore tags?

Returns:

  • (Boolean)


217
218
219
# File 'lib/puppet/transaction.rb', line 217

def ignore_tags?
  ! (@catalog.host_config? or Puppet[:name] == "puppet")
end

#make_parent_child_relationship(resource, children) ⇒ Object

Copy an important relationships from the parent to the newly-generated child resource.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/puppet/transaction.rb', line 62

def make_parent_child_relationship(resource, children)
  depthfirst = resource.depthfirst?

  children.each do |gen_child|
    if depthfirst
      edge = [gen_child, resource]
    else
      edge = [resource, gen_child]
    end
    relationship_graph.add_vertex(gen_child)

    unless relationship_graph.edge?(edge[1], edge[0])
      relationship_graph.add_edge(*edge)
    else
      resource.debug "Skipping automatic relationship to #{gen_child}"
    end
  end
end

#missing_tags?(resource) ⇒ Boolean

Is this resource tagged appropriately?

Returns:

  • (Boolean)


314
315
316
317
318
319
# File 'lib/puppet/transaction.rb', line 314

def missing_tags?(resource)
  return false if ignore_tags?
  return false if tags.empty?

  not resource.tagged?(*tags)
end

#prefetchObject

Prefetch any providers that support it. We don’t support prefetching types, just providers.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/puppet/transaction.rb', line 232

def prefetch
  prefetchers = {}
  @catalog.vertices.each do |resource|
    if provider = resource.provider and provider.class.respond_to?(:prefetch)
      prefetchers[provider.class] ||= {}
      prefetchers[provider.class][resource.name] = resource
    end
  end

  # Now call prefetch, passing in the resources so that the provider instances can be replaced.
  prefetchers.each do |provider, resources|
    Puppet.debug "Prefetching #{provider.name} resources for #{provider.resource_type.name}"
    begin
      provider.prefetch(resources)
    rescue => detail
      puts detail.backtrace if Puppet[:trace]
      Puppet.err "Could not prefetch #{provider.resource_type.name} provider '#{provider.name}': #{detail}"
    end
  end
end

#prepareObject

Prepare to evaluate the resources in a transaction.



254
255
256
257
258
259
260
261
262
263
264
# File 'lib/puppet/transaction.rb', line 254

def prepare
  # Now add any dynamically generated resources
  generate

  # Then prefetch.  It's important that we generate and then prefetch,
  # so that any generated resources also get prefetched.
  prefetch

  # This will throw an error if there are cycles in the graph.
  @sorted_resources = relationship_graph.topsort
end

#relationship_graphObject



266
267
268
# File 'lib/puppet/transaction.rb', line 266

def relationship_graph
  catalog.relationship_graph
end

#resource_status(resource) ⇒ Object



274
275
276
# File 'lib/puppet/transaction.rb', line 274

def resource_status(resource)
  report.resource_statuses[resource.to_s] || add_resource_status(Puppet::Resource::Status.new(resource))
end

#scheduled?(resource) ⇒ Boolean

Is the resource currently scheduled?

Returns:

  • (Boolean)


279
280
281
# File 'lib/puppet/transaction.rb', line 279

def scheduled?(resource)
  self.ignoreschedules or resource_harness.scheduled?(resource_status(resource), resource)
end

#skip?(resource) ⇒ Boolean

Should this resource be skipped?

Returns:

  • (Boolean)


284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/puppet/transaction.rb', line 284

def skip?(resource)
  if missing_tags?(resource)
    resource.debug "Not tagged with #{tags.join(", ")}"
  elsif ! scheduled?(resource)
    resource.debug "Not scheduled"
  elsif failed_dependencies?(resource)
    resource.warning "Skipping because of failed dependencies"
  elsif resource.virtual?
    resource.debug "Skipping because virtual"
  else
    return false
  end
  true
end

#stop_processing?Boolean

Wraps application run state check to flag need to interrupt processing

Returns:

  • (Boolean)


30
31
32
# File 'lib/puppet/transaction.rb', line 30

def stop_processing?
  Puppet::Application.stop_requested?
end

#tagsObject

The tags we should be checking.



300
301
302
303
304
# File 'lib/puppet/transaction.rb', line 300

def tags
  self.tags = Puppet[:tags] unless defined?(@tags)

  super
end