Class: Puppet::Transaction

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

Defined Under Namespace

Classes: Event, EventManager, Relationship_graph_wrapper, Report, ResourceHarness

Constant Summary

Constants included from Util

Util::AbsolutePathPosix, Util::AbsolutePathWindows

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util::Tagging

#tag, #tagged?, #tags=

Methods included from Util

absolute_path?, activerecord_version, benchmark, binread, chuser, classproxy, #execfail, #execpipe, execute, execute_posix, execute_windows, logmethods, memory, path_to_uri, proxy, replace_file, safe_posix_fork, symbolize, symbolizehash, symbolizehash!, synchronize_on, thinmark, #threadlock, uri_to_path, wait_for_output, 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



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/vendor/puppet/transaction.rb', line 235

def initialize(catalog, report = nil)
  @catalog = catalog

  @report = report || Puppet::Transaction::Report.new("apply", catalog.version, Puppet[:environment])

  @event_manager = Puppet::Transaction::EventManager.new(self)

  @resource_harness = Puppet::Transaction::ResourceHarness.new(self)

  @prefetched_providers = Hash.new { |h,k| h[k] = {} }
end

Instance Attribute Details

#catalogObject

Returns the value of attribute catalog.



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

def catalog
  @catalog
end

#componentObject

Returns the value of attribute component.



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

def component
  @component
end

#configuratorObject

Returns the value of attribute configurator.



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

def configurator
  @configurator
end

#event_managerObject (readonly)

Routes and stores any events and subscriptions.



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

def event_manager
  @event_manager
end

#for_network_deviceObject

Returns the value of attribute for_network_device.



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

def for_network_device
  @for_network_device
end

#ignoreschedulesObject

Returns the value of attribute ignoreschedules.



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

def ignoreschedules
  @ignoreschedules
end

#prefetched_providersObject (readonly)

Returns the value of attribute prefetched_providers.



277
278
279
# File 'lib/vendor/puppet/transaction.rb', line 277

def prefetched_providers
  @prefetched_providers
end

#reportObject (readonly)

The report, once generated.



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

def report
  @report
end

#resource_harnessObject (readonly)

Handles most of the actual interacting with resources



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

def resource_harness
  @resource_harness
end

Instance Method Details

#add_conditional_directed_dependency(parent, child, label = nil) ⇒ Object

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



68
69
70
71
72
73
74
75
76
# File 'lib/vendor/puppet/transaction.rb', line 68

def add_conditional_directed_dependency(parent, child, label=nil)
  relationship_graph.add_vertex(child)
  edge = parent.depthfirst? ? [child, parent] : [parent, child]
  if relationship_graph.edge?(*edge.reverse)
    parent.debug "Skipping automatic relationship to #{child}"
  else
    relationship_graph.add_edge(edge[0],edge[1],label)
  end
end

#add_dynamically_generated_resourcesObject



224
225
226
# File 'lib/vendor/puppet/transaction.rb', line 224

def add_dynamically_generated_resources
  @catalog.vertices.each { |resource| generate_additional_resources(resource) }
end

#add_resource_status(status) ⇒ Object



432
433
434
# File 'lib/vendor/puppet/transaction.rb', line 432

def add_resource_status(status)
  report.add_resource_status status
end

#add_times(hash) ⇒ Object

Add some additional times for reporting



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

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)


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

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

#applied_resourcesObject

Find all of the applied resources (including failed attempts).



62
63
64
# File 'lib/vendor/puppet/transaction.rb', line 62

def applied_resources
  report.resource_statuses.values.collect { |status| catalog.resource(status.resource) }
end

#apply(resource, ancestor = nil) ⇒ Object

Apply all changes for a resource



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

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)


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

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

#eval_generate(resource) ⇒ Object

Raises:



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

def eval_generate(resource)
  return false unless resource.respond_to?(:eval_generate)
  raise Puppet::DevError,"Depthfirst resources are not supported by eval_generate" if resource.depthfirst?
  begin
    made = resource.eval_generate.uniq
    return false if made.empty?
    made = made.inject({}) {|a,v| a.merge(v.name => v) }
  rescue => detail
    puts detail.backtrace if Puppet[:trace]
    resource.err "Failed to generate additional resources using 'eval_generate: #{detail}"
    return false
  end
  made.values.each do |res|
    begin
      res.tag(*resource.tags)
      @catalog.add_resource(res)
      res.finish
    rescue Puppet::Resource::Catalog::DuplicateResourceError
      res.info "Duplicate generated resource; skipping"
    end
  end
  sentinel = Puppet::Type.type(:whit).new(:name => "completed_#{resource.title}", :catalog => resource.catalog)

  # The completed whit is now the thing that represents the resource is done
  relationship_graph.adjacent(resource,:direction => :out,:type => :edges).each { |e|
    # But children run as part of the resource, not after it
    next if made[e.target.name]

    add_conditional_directed_dependency(sentinel, e.target, e.label)
    relationship_graph.remove_edge! e
  }

  default_label = Puppet::Resource::Catalog::Default_label
  made.values.each do |res|
    # Depend on the nearest ancestor we generated, falling back to the
    # resource if we have none
    parent_name = res.ancestors.find { |a| made[a] and made[a] != res }
    parent = made[parent_name] || resource

    add_conditional_directed_dependency(parent, res)

    # This resource isn't 'completed' until each child has run
    add_conditional_directed_dependency(res, sentinel, default_label)
  end

  # This edge allows the resource's events to propagate, though it isn't
  # strictly necessary for ordering purposes
  add_conditional_directed_dependency(resource, sentinel, default_label)
  true
end

#eval_resource(resource, ancestor = nil) ⇒ Object

Evaluate a single resource.



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/vendor/puppet/transaction.rb', line 79

def eval_resource(resource, ancestor = nil)
  if skip?(resource)
    resource_status(resource).skipped = true
  else
    resource_status(resource).scheduled = true
    apply(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.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/vendor/puppet/transaction.rb', line 94

def evaluate
  add_dynamically_generated_resources

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

  relationship_graph.traverse do |resource|
    if resource.is_a?(Puppet::Type::Component)
      Puppet.warning "Somehow left a component in the relationship graph"
    else
      resource.info "Starting to evaluate the resource" if Puppet[:evaltrace] and @catalog.host_config?
      seconds = thinmark { eval_resource(resource) }
      resource.info "Evaluated in %0.2f seconds" % seconds if Puppet[:evaltrace] and @catalog.host_config?
    end
  end

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

#eventsObject



112
113
114
# File 'lib/vendor/puppet/transaction.rb', line 112

def events
  event_manager.events
end

#failed?(resource) ⇒ Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/vendor/puppet/transaction.rb', line 116

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

#failed_dependencies?(resource) ⇒ Boolean

Does this resource have any failed dependencies?

Returns:

  • (Boolean)


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

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


  # When we introduced the :whit into the graph, to reduce the combinatorial
  # explosion of edges, we also ended up reporting failures for containers
  # like class and stage.  This is undesirable; while just skipping the
  # output isn't perfect, it is RC-safe. --daniel 2011-06-07
  suppress_report = (resource.class == Puppet::Type.type(:whit))

  relationship_graph.dependencies(resource).each do |dep|
    next unless failed?(dep)
    found_failed = true

    # See above. --daniel 2011-06-06
    unless suppress_report then
      resource.notice "Dependency #{dep} has failures: #{resource_status(dep).failed}"
    end
  end

  found_failed
end

#generate_additional_resources(resource) ⇒ Object

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



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/vendor/puppet/transaction.rb', line 201

def generate_additional_resources(resource)
  return unless resource.respond_to?(:generate)
  begin
    made = resource.generate
  rescue => detail
    puts detail.backtrace if Puppet[:trace]
    resource.err "Failed to generate additional resources using 'generate': #{detail}"
  end
  return unless made
  made = [made] unless made.is_a?(Array)
  made.uniq.each do |res|
    begin
      res.tag(*resource.tags)
      @catalog.add_resource(res)
      res.finish
      add_conditional_directed_dependency(resource, res)
      generate_additional_resources(res)
    rescue Puppet::Resource::Catalog::DuplicateResourceError
      res.info "Duplicate generated resource; skipping"
    end
  end
end

#handle_qualified_tags(qualified) ⇒ Object



476
477
478
479
480
481
# File 'lib/vendor/puppet/transaction.rb', line 476

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)


229
230
231
# File 'lib/vendor/puppet/transaction.rb', line 229

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

#missing_tags?(resource) ⇒ Boolean

Is this resource tagged appropriately?

Returns:

  • (Boolean)


484
485
486
487
488
489
# File 'lib/vendor/puppet/transaction.rb', line 484

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

  not resource.tagged?(*tags)
end

#prefetch(provider_class, resources) ⇒ Object

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



281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/vendor/puppet/transaction.rb', line 281

def prefetch(provider_class, resources)
  type_name = provider_class.resource_type.name
  return if @prefetched_providers[type_name][provider_class.name]
  Puppet.debug "Prefetching #{provider_class.name} resources for #{type_name}"
  begin
    provider_class.prefetch(resources)
  rescue => detail
    puts detail.backtrace if Puppet[:trace]
    Puppet.err "Could not prefetch #{type_name} provider '#{provider_class.name}': #{detail}"
  end
  @prefetched_providers[type_name][provider_class.name] = true
end

#prefetch_if_necessary(resource) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/vendor/puppet/transaction.rb', line 262

def prefetch_if_necessary(resource)
  provider_class = resource.provider.class
  return unless provider_class.respond_to?(:prefetch) and !prefetched_providers[resource.type][provider_class.name]

  resources = resources_by_provider(resource.type, provider_class.name)

  if provider_class == resource.class.defaultprovider
    providerless_resources = resources_by_provider(resource.type, nil)
    providerless_resources.values.each {|res| res.provider = provider_class.name}
    resources.merge! providerless_resources
  end

  prefetch(provider_class, resources)
end

#relationship_graphObject



428
429
430
# File 'lib/vendor/puppet/transaction.rb', line 428

def relationship_graph
  @relationship_graph ||= Relationship_graph_wrapper.new(catalog.relationship_graph,self)
end

#resource_status(resource) ⇒ Object



436
437
438
# File 'lib/vendor/puppet/transaction.rb', line 436

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

#resources_by_provider(type_name, provider_name) ⇒ Object



247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/vendor/puppet/transaction.rb', line 247

def resources_by_provider(type_name, provider_name)
  unless @resources_by_provider
    @resources_by_provider = Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = {} } }

    @catalog.vertices.each do |resource|
      if resource.class.attrclass(:provider)
        prov = resource.provider && resource.provider.class.name
        @resources_by_provider[resource.type][prov][resource.name] = resource
      end
    end
  end

  @resources_by_provider[type_name][provider_name] || {}
end

#scheduled?(resource) ⇒ Boolean

Is the resource currently scheduled?

Returns:

  • (Boolean)


441
442
443
# File 'lib/vendor/puppet/transaction.rb', line 441

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

#skip?(resource) ⇒ Boolean

Should this resource be skipped?

Returns:

  • (Boolean)


446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/vendor/puppet/transaction.rb', line 446

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)
    # When we introduced the :whit into the graph, to reduce the combinatorial
    # explosion of edges, we also ended up reporting failures for containers
    # like class and stage.  This is undesirable; while just skipping the
    # output isn't perfect, it is RC-safe. --daniel 2011-06-07
    unless resource.class == Puppet::Type.type(:whit) then
      resource.warning "Skipping because of failed dependencies"
    end
  elsif resource.virtual?
    resource.debug "Skipping because virtual"
  elsif resource.appliable_to_device? ^ for_network_device
    resource.debug "Skipping #{resource.appliable_to_device? ? 'device' : 'host'} resources because running on a #{for_network_device ? 'device' : 'host'}"
  else
    return false
  end
  true
end

#stop_processing?Boolean

Wraps application run state check to flag need to interrupt processing

Returns:

  • (Boolean)


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

def stop_processing?
  Puppet::Application.stop_requested?
end

#tagsObject

The tags we should be checking.



470
471
472
473
474
# File 'lib/vendor/puppet/transaction.rb', line 470

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

  super
end