class Transaction

Public Class Methods

new(catalog, report = nil) click to toggle source

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

# File lib/puppet/transaction.rb, line 232
def initialize(catalog, report = nil)
  @catalog = catalog

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

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

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

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

Public Instance Methods

add_conditional_directed_dependency(parent, child, label=nil) click to toggle source

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

# File lib/puppet/transaction.rb, line 67
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_resources() click to toggle source
# File lib/puppet/transaction.rb, line 221
def add_dynamically_generated_resources
  @catalog.vertices.each { |resource| generate_additional_resources(resource) }
end
add_resource_status(status) click to toggle source
# File lib/puppet/transaction.rb, line 428
def add_resource_status(status)
  report.add_resource_status status
end
add_times(hash) click to toggle source

Add some additional times for reporting

# 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?() click to toggle source

Are there any failed resources in this transaction?

# File lib/puppet/transaction.rb, line 42
def any_failed?
  report.resource_statuses.values.detect { |status| status.failed? }
end
applied_resources() click to toggle source

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

# File lib/puppet/transaction.rb, line 61
def applied_resources
  report.resource_statuses.values.collect { |status| catalog.resource(status.resource) }
end
apply(resource, ancestor = nil) click to toggle source

Apply all changes for a resource

# 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?() click to toggle source

Find all of the changed resources.

# 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_generate(resource) click to toggle source
# File lib/puppet/transaction.rb, line 147
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 = Hash[made.map(&:name).zip(made)]
  rescue => detail
    resource.log_exception(detail, "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) click to toggle source

Evaluate a single resource.

# File lib/puppet/transaction.rb, line 78
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
evaluate() click to toggle source

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.

# File lib/puppet/transaction.rb, line 93
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
events() click to toggle source
# File lib/puppet/transaction.rb, line 111
def events
  event_manager.events
end
failed?(resource) click to toggle source
# File lib/puppet/transaction.rb, line 115
def failed?(resource)
  s = resource_status(resource) and s.failed?
end
failed_dependencies?(resource) click to toggle source

Does this resource have any failed dependencies?

# File lib/puppet/transaction.rb, line 120
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) click to toggle source

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

# File lib/puppet/transaction.rb, line 199
def generate_additional_resources(resource)
  return unless resource.respond_to?(:generate)
  begin
    made = resource.generate
  rescue => detail
    resource.log_exception(detail, "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 ) click to toggle source
# File lib/puppet/transaction.rb, line 472
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?() click to toggle source

Should we ignore tags?

# File lib/puppet/transaction.rb, line 226
def ignore_tags?
  ! @catalog.host_config? 
end
missing_tags?(resource) click to toggle source

Is this resource tagged appropriately?

# File lib/puppet/transaction.rb, line 480
def missing_tags?(resource)
  return false if ignore_tags?
  return false if tags.empty?

  not resource.tagged?(*tags)
end
prefetch(provider_class, resources) click to toggle source

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

# File lib/puppet/transaction.rb, line 278
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
    Puppet.log_exception(detail, "Could not prefetch #{type_name} provider '#{provider_class.name}': #{detail}")
  end
  @prefetched_providers[type_name][provider_class.name] = true
end
prefetch_if_necessary(resource) click to toggle source
# File lib/puppet/transaction.rb, line 259
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_graph() click to toggle source
# File lib/puppet/transaction.rb, line 424
def relationship_graph
  @relationship_graph ||= Relationship_graph_wrapper.new(catalog.relationship_graph,self)
end
resource_status(resource) click to toggle source
# File lib/puppet/transaction.rb, line 432
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) click to toggle source
# File lib/puppet/transaction.rb, line 244
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) click to toggle source

Is the resource currently scheduled?

# File lib/puppet/transaction.rb, line 437
def scheduled?(resource)
  self.ignoreschedules or resource_harness.scheduled?(resource_status(resource), resource)
end
skip?(resource) click to toggle source

Should this resource be skipped?

# File lib/puppet/transaction.rb, line 442
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?() click to toggle source

Wraps application run state check to flag need to interrupt processing

# File lib/puppet/transaction.rb, line 30
def stop_processing?
  Puppet::Application.stop_requested?
end
tags() click to toggle source

The tags we should be checking.

Calls superclass method Puppet::Util::Tagging#tags
# File lib/puppet/transaction.rb, line 466
def tags
  self.tags = Puppet[:tags] unless defined?(@tags)

  super
end