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
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
# File lib/puppet/transaction.rb, line 221 def add_dynamically_generated_resources @catalog.vertices.each { |resource| generate_additional_resources(resource) } end
# File lib/puppet/transaction.rb, line 428 def add_resource_status(status) report.add_resource_status status end
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
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
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 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
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
# 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
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
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
# File lib/puppet/transaction.rb, line 111 def events event_manager.events end
# File lib/puppet/transaction.rb, line 115 def failed?(resource) s = resource_status(resource) and s.failed? end
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
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
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
# 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
# File lib/puppet/transaction.rb, line 424 def relationship_graph @relationship_graph ||= Relationship_graph_wrapper.new(catalog.relationship_graph,self) end
# 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
# 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
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
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
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