diff options
Diffstat (limited to 'lib/puppet/parser/compile.rb')
-rw-r--r-- | lib/puppet/parser/compile.rb | 467 |
1 files changed, 0 insertions, 467 deletions
diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb deleted file mode 100644 index 2415fd5e8..000000000 --- a/lib/puppet/parser/compile.rb +++ /dev/null @@ -1,467 +0,0 @@ -# Created by Luke A. Kanies on 2007-08-13. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/node' -require 'puppet/node/catalog' -require 'puppet/util/errors' - -# Maintain a graph of scopes, along with a bunch of data -# about the individual catalog we're compiling. -class Puppet::Parser::Compile - include Puppet::Util - include Puppet::Util::Errors - attr_reader :parser, :node, :facts, :collections, :catalog, :node_scope - - # Add a collection to the global list. - def add_collection(coll) - @collections << coll - end - - # Store a resource override. - def add_override(override) - # If possible, merge the override in immediately. - if resource = @catalog.resource(override.ref) - resource.merge(override) - else - # Otherwise, store the override for later; these - # get evaluated in Resource#finish. - @resource_overrides[override.ref] << override - end - end - - # Store a resource in our resource table. - def add_resource(scope, resource) - @catalog.add_resource(resource) - - # And in the resource graph. At some point, this might supercede - # the global resource table, but the table is a lot faster - # so it makes sense to maintain for now. - @catalog.add_edge!(scope.resource, resource) - end - - # Do we use nodes found in the code, vs. the external node sources? - def ast_nodes? - parser.nodes.length > 0 - end - - # Store the fact that we've evaluated a class, and store a reference to - # the scope in which it was evaluated, so that we can look it up later. - def class_set(name, scope) - if existing = @class_scopes[name] - if existing.nodescope? or scope.nodescope? - raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" - else - raise Puppet::DevError, "Somehow evaluated the same class twice" - end - end - @class_scopes[name] = scope - @catalog.add_class(name) unless name == "" - end - - # Return the scope associated with a class. This is just here so - # that subclasses can set their parent scopes to be the scope of - # their parent class, and it's also used when looking up qualified - # variables. - def class_scope(klass) - # They might pass in either the class or class name - if klass.respond_to?(:classname) - @class_scopes[klass.classname] - else - @class_scopes[klass] - end - end - - # Return a list of all of the defined classes. - def classlist - return @catalog.classes - end - - # Compile our catalog. This mostly revolves around finding and evaluating classes. - # This is the main entry into our catalog. - def compile - # Set the client's parameters into the top scope. - set_node_parameters() - - evaluate_main() - - evaluate_ast_node() - - evaluate_node_classes() - - evaluate_generators() - - finish() - - fail_on_unevaluated() - - if Puppet[:storeconfigs] - store() - end - - return @catalog - end - - # LAK:FIXME There are no tests for this. - def delete_collection(coll) - @collections.delete(coll) if @collections.include?(coll) - end - - # Return the node's environment. - def environment - unless defined? @environment - if node.environment and node.environment != "" - @environment = node.environment - else - @environment = nil - end - end - @environment - end - - # Evaluate all of the classes specified by the node. - def evaluate_node_classes - evaluate_classes(@node.classes, topscope) - end - - # Evaluate each specified class in turn. If there are any classes we can't - # find, just tag the catalog and move on. This method really just - # creates resource objects that point back to the classes, and then the - # resources are themselves evaluated later in the process. - def evaluate_classes(classes, scope, lazy_evaluate = true) - unless scope.source - raise Puppet::DevError, "No source for scope passed to evaluate_classes" - end - found = [] - classes.each do |name| - # If we can find the class, then make a resource that will evaluate it. - if klass = scope.findclass(name) - found << name and next if class_scope(klass) - - resource = klass.evaluate(scope) - - # If they've disabled lazy evaluation (which the :include function does), - # then evaluate our resource immediately. - resource.evaluate unless lazy_evaluate - found << name - else - Puppet.info "Could not find class %s for %s" % [name, node.name] - @catalog.tag(name) - end - end - found - end - - # Return a resource by either its ref or its type and title. - def findresource(*args) - @catalog.resource(*args) - end - - # Set up our compile. We require a parser - # and a node object; the parser is so we can look up classes - # and AST nodes, and the node has all of the client's info, - # like facts and environment. - def initialize(node, parser, options = {}) - @node = node - @parser = parser - - options.each do |param, value| - begin - send(param.to_s + "=", value) - rescue NoMethodError - raise ArgumentError, "Compile objects do not accept %s" % param - end - end - - initvars() - init_main() - end - - # Create a new scope, with either a specified parent scope or - # using the top scope. Adds an edge between the scope and - # its parent to the graph. - def newscope(parent, options = {}) - parent ||= topscope - options[:compile] = self - options[:parser] ||= self.parser - scope = Puppet::Parser::Scope.new(options) - @scope_graph.add_edge!(parent, scope) - scope - end - - # Find the parent of a given scope. Assumes scopes only ever have - # one in edge, which will always be true. - def parent(scope) - if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0 - ary[0] - else - nil - end - end - - # Return any overrides for the given resource. - def resource_overrides(resource) - @resource_overrides[resource.ref] - end - - # Return a list of all resources. - def resources - @catalog.vertices - end - - # The top scope is usually the top-level scope, but if we're using AST nodes, - # then it is instead the node's scope. - def topscope - node_scope || @topscope - end - - private - - # If ast nodes are enabled, then see if we can find and evaluate one. - def evaluate_ast_node - return unless ast_nodes? - - # Now see if we can find the node. - astnode = nil - @node.names.each do |name| - break if astnode = @parser.nodes[name.to_s.downcase] - end - - unless (astnode ||= @parser.nodes["default"]) - raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") - end - - # Create a resource to model this node, and then add it to the list - # of resources. - resource = astnode.evaluate(topscope) - - resource.evaluate - - # Now set the node scope appropriately, so that :topscope can - # behave differently. - @node_scope = class_scope(astnode) - end - - # Evaluate our collections and return true if anything returned an object. - # The 'true' is used to continue a loop, so it's important. - def evaluate_collections - return false if @collections.empty? - - found_something = false - exceptwrap do - # We have to iterate over a dup of the array because - # collections can delete themselves from the list, which - # changes its length and causes some collections to get missed. - @collections.dup.each do |collection| - found_something = true if collection.evaluate - end - end - - return found_something - end - - # Make sure all of our resources have been evaluated into native resources. - # We return true if any resources have, so that we know to continue the - # evaluate_generators loop. - def evaluate_definitions - exceptwrap do - if ary = unevaluated_resources - ary.each do |resource| - resource.evaluate - end - # If we evaluated, let the loop know. - return true - else - return false - end - end - end - - # Iterate over collections and resources until we're sure that the whole - # compile is evaluated. This is necessary because both collections - # and defined resources can generate new resources, which themselves could - # be defined resources. - def evaluate_generators - count = 0 - loop do - done = true - - # Call collections first, then definitions. - done = false if evaluate_collections - done = false if evaluate_definitions - break if done - - count += 1 - - if count > 1000 - raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" - end - end - end - - # Find and evaluate our main object, if possible. - def evaluate_main - @main = @parser.findclass("", "") || @parser.newclass("") - @topscope.source = @main - @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) - @topscope.resource = @main_resource - - @catalog.add_resource(@main_resource) - - @main_resource.evaluate - end - - # Make sure the entire catalog is evaluated. - def fail_on_unevaluated - fail_on_unevaluated_overrides - fail_on_unevaluated_resource_collections - end - - # If there are any resource overrides remaining, then we could - # not find the resource they were supposed to override, so we - # want to throw an exception. - def fail_on_unevaluated_overrides - remaining = [] - @resource_overrides.each do |name, overrides| - remaining += overrides - end - - unless remaining.empty? - fail Puppet::ParseError, - "Could not find object(s) %s" % remaining.collect { |o| - o.ref - }.join(", ") - end - end - - # Make sure we don't have any remaining collections that specifically - # look for resources, because we want to consider those to be - # parse errors. - def fail_on_unevaluated_resource_collections - remaining = [] - @collections.each do |coll| - # We're only interested in the 'resource' collections, - # which result from direct calls of 'realize'. Anything - # else is allowed not to return resources. - # Collect all of them, so we have a useful error. - if r = coll.resources - if r.is_a?(Array) - remaining += r - else - remaining << r - end - end - end - - unless remaining.empty? - raise Puppet::ParseError, "Failed to realize virtual resources %s" % - remaining.join(', ') - end - end - - # Make sure all of our resources and such have done any last work - # necessary. - def finish - @catalog.resources.each do |name| - resource = @catalog.resource(name) - - # Add in any resource overrides. - if overrides = resource_overrides(resource) - overrides.each do |over| - resource.merge(over) - end - - # Remove the overrides, so that the configuration knows there - # are none left. - overrides.clear - end - - resource.finish if resource.respond_to?(:finish) - end - end - - # Initialize the top-level scope, class, and resource. - def init_main - # Create our initial scope and a resource that will evaluate main. - @topscope = Puppet::Parser::Scope.new(:compile => self, :parser => self.parser) - @scope_graph.add_vertex!(@topscope) - end - - # Set up all of our internal variables. - def initvars - # The table for storing class singletons. This will only actually - # be used by top scopes and node scopes. - @class_scopes = {} - - # The list of objects that will available for export. - @exported_resources = {} - - # The list of overrides. This is used to cache overrides on objects - # that don't exist yet. We store an array of each override. - @resource_overrides = Hash.new do |overs, ref| - overs[ref] = [] - end - - # The list of collections that have been created. This is a global list, - # but they each refer back to the scope that created them. - @collections = [] - - # A graph for maintaining scope relationships. - @scope_graph = Puppet::SimpleGraph.new - - # For maintaining the relationship between scopes and their resources. - @catalog = Puppet::Node::Catalog.new(@node.name) - @catalog.version = @parser.version - end - - # Set the node's parameters into the top-scope as variables. - def set_node_parameters - node.parameters.each do |param, value| - @topscope.setvar(param, value) - end - end - - # Store the catalog into the database. - def store - unless Puppet.features.rails? - raise Puppet::Error, - "storeconfigs is enabled but rails is unavailable" - end - - unless ActiveRecord::Base.connected? - Puppet::Rails.connect - end - - # We used to have hooks here for forking and saving, but I don't - # think it's worth retaining at this point. - store_to_active_record(@node, @catalog.vertices) - end - - # Do the actual storage. - def store_to_active_record(node, resources) - begin - # We store all of the objects, even the collectable ones - benchmark(:info, "Stored catalog for #{node.name}") do - Puppet::Rails::Host.transaction do - Puppet::Rails::Host.store(node, resources) - end - end - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err "Could not store configs: %s" % detail.to_s - end - end - - # Return an array of all of the unevaluated resources. These will be definitions, - # which need to get evaluated into native resources. - def unevaluated_resources - ary = @catalog.vertices.reject { |resource| resource.builtin? or resource.evaluated? } - - if ary.empty? - return nil - else - return ary - end - end -end |