summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/parser')
-rw-r--r--lib/puppet/parser/configuration.rb428
-rw-r--r--lib/puppet/parser/interpreter.rb401
-rw-r--r--lib/puppet/parser/parser_support.rb2
-rw-r--r--lib/puppet/parser/resource.rb5
-rw-r--r--lib/puppet/parser/scope.rb149
5 files changed, 431 insertions, 554 deletions
diff --git a/lib/puppet/parser/configuration.rb b/lib/puppet/parser/configuration.rb
index c7979e51f..617d7d231 100644
--- a/lib/puppet/parser/configuration.rb
+++ b/lib/puppet/parser/configuration.rb
@@ -5,20 +5,33 @@ require 'puppet/external/gratr/digraph'
require 'puppet/external/gratr/import'
require 'puppet/external/gratr/dot'
+require 'puppet/util/errors'
+
# Maintain a graph of scopes, along with a bunch of data
# about the individual configuration we're compiling.
class Puppet::Parser::Configuration
- attr_reader :topscope, :interpreter, :host, :facts
+ include Puppet::Util
+ include Puppet::Util::Errors
+ attr_reader :topscope, :parser, :node, :facts
+ attr_accessor :extraction_format
+
+ attr_writer :ast_nodes
# Add a collection to the global list.
def add_collection(coll)
@collections << coll
end
+ # Do we use nodes found in the code, vs. the external node sources?
+ def ast_nodes?
+ defined?(@ast_nodes) and @ast_nodes
+ 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)
@class_scopes[name] = scope
+ tag(name)
end
# Return the scope associated with a class. This is just here so
@@ -39,22 +52,66 @@ class Puppet::Parser::Configuration
return @class_scopes.keys.reject { |k| k == "" }
end
+ # Compile our configuration. This mostly revolves around finding and evaluating classes.
+ # This is the main entry into our configuration.
+ def compile
+ # Set the client's parameters into the top scope.
+ set_node_parameters()
+
+ evaluate_main()
+
+ evaluate_ast_nodes()
+
+ evaluate_classes()
+
+ evaluate_generators()
+
+ fail_on_unevaluated()
+
+ finish()
+
+ return extract()
+ end
+
# Should the scopes behave declaratively?
def declarative?
true
end
- # Set up our configuration. We require an interpreter
- # and a host name, and we normally are passed facts, too.
- def initialize(options)
- @interpreter = options[:interpreter] or
- raise ArgumentError, "You must pass an interpreter to the configuration"
- @facts = options[:facts] || {}
- @host = options[:host] or
- raise ArgumentError, "You must pass a host name to the configuration"
+ # Make sure we support the requested extraction format.
+ def extraction_format=(value)
+ unless respond_to?("extract_to_%s" % value)
+ raise ArgumentError, "Invalid extraction format %s" % value
+ end
+ @extraction_format = value
+ end
+
+ # Return a resource by either its ref or its type and title.
+ def findresource(string, name = nil)
+ if name
+ string = "%s[%s]" % [string.capitalize, name]
+ end
+
+ @resource_table[string]
+ end
+
+ # Set up our configuration. 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, "Configuration objects do not accept %s" % param
+ end
+ end
- # Call the setup methods from the base class.
- super()
+ @extraction_format ||= :transportable
initvars()
end
@@ -65,35 +122,254 @@ class Puppet::Parser::Configuration
def newscope(parent = nil)
parent ||= @topscope
scope = Puppet::Parser::Scope.new(:configuration => self)
- @graph.add_edge!(parent, scope)
+ @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 = @graph.adjacent(scope, :direction => :in) and ary.length > 0
+ if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0
ary[0]
else
nil
end
end
- # Return an array of all of the unevaluated objects
- def unevaluated
- ary = @definedtable.find_all do |name, object|
- ! object.builtin? and ! object.evaluated?
- end.collect { |name, object| object }
+ # Return any overrides for the given resource.
+ def resource_overrides(resource)
+ @resource_overrides[resource.ref]
+ end
- if ary.empty?
- return nil
+ # Store a resource override.
+ def store_override(override)
+ override.override = true
+
+ # If possible, merge the override in immediately.
+ if resource = @resource_table[override.ref]
+ resource.merge(override)
else
- return ary
+ # 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 store_resource(scope, resource)
+ # This might throw an exception
+ verify_uniqueness(resource)
+
+ # Store it in the global table.
+ @resource_table[resource.ref] = 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.
+ @resource_graph.add_edge!(scope, resource)
+ 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
+ #nodes = @parser.nodes
+ @node.names.each do |name|
+ break if astnode = @parser.nodes[name]
+ end
+
+ unless astnode
+ astnode = @parser.nodes["default"]
+ end
+ unless astnode
+ raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ")
+ end
+
+ astnode.safeevaluate :scope => topscope
+ end
+
+ # Evaluate each class in turn. If there are any classes we can't find,
+ # just tag the configuration and move on.
+ def evaluate_classes
+ node.classes.each do |name|
+ if klass = @parser.findclass("", name)
+ # This will result in class_set getting called, which
+ # will in turn result in tags. Yay.
+ klass.safeevaluate(:scope => topscope)
+ else
+ Puppet.info "Could not find class %s for %s" % [name, node.name]
+ tag(name)
+ end
+ end
+ 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
+ @collections.each do |collection|
+ if collection.evaluate
+ found_something = true
+ end
+ 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
+ # configuration 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
+ if count > 1000
+ raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host configuration"
+ end
+ end
+ end
+
+ # Find and evaluate our main object, if possible.
+ def evaluate_main
+ if klass = @parser.findclass("", "")
+ # Set the source, so objects can tell where they were defined.
+ topscope.source = klass
+ klass.safeevaluate :scope => topscope, :nosubscope => true
+ end
+ end
+
+ # Turn our configuration graph into whatever the client is expecting.
+ def extract
+ send("extract_to_%s" % extraction_format)
+ end
+
+ # Create the traditional TransBuckets and TransObjects from our configuration
+ # graph. This will hopefully be deprecated soon.
+ def extract_to_transportable
+ top = nil
+ current = nil
+ buckets = {}
+
+ # I'm *sure* there's a simple way to do this using a breadth-first search
+ # or something, but I couldn't come up with, and this is both fast
+ # and simple, so I'm not going to worry about it too much.
+ @scope_graph.vertices.each do |scope|
+ # For each scope, we need to create a TransBucket, and then
+ # put all of the scope's resources into that bucket, translating
+ # each resource into a TransObject.
+
+ # Unless the bucket's already been created, make it now and add
+ # it to the cache.
+ unless bucket = buckets[scope]
+ bucket = buckets[scope] = scope.to_trans
+ end
+
+ # First add any contained scopes
+ @scope_graph.adjacent(scope, :direction => :out).each do |vertex|
+ # If there's not already a bucket, then create and cache it.
+ unless child_bucket = buckets[vertex]
+ child_bucket = buckets[vertex] = vertex.to_trans
+ end
+ bucket.push child_bucket
+ end
+
+ # Then add the resources.
+ @resource_graph.adjacent(scope, :direction => :out).each do |vertex|
+ bucket.push vertex.to_trans
+ end
+ end
+
+ # Clear the cache to encourage the GC
+ result = buckets[topscope]
+ buckets.clear
+ return result
+ end
+
+ # Make sure the entire configuration 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
+ @resource_table.each { |name, resource| resource.finish if resource.respond_to?(:finish) }
+ end
+
# Set up all of our internal variables.
def initvars
# The table for storing class singletons. This will only actually
@@ -116,18 +392,114 @@ class Puppet::Parser::Configuration
# but they each refer back to the scope that created them.
@collections = []
+ # A list of tags we've generated; most class names.
+ @tags = []
+
# Create our initial scope, our scope graph, and add the initial scope to the graph.
@topscope = Puppet::Parser::Scope.new(:configuration => self, :type => "main", :name => "top")
- @graph = GRATR::Digraph.new
- @graph.add_vertex!(@topscope)
+
+ # For maintaining scope relationships.
+ @scope_graph = GRATR::Digraph.new
+ @scope_graph.add_vertex!(@topscope)
+
+ # For maintaining the relationship between scopes and their resources.
+ @resource_graph = GRATR::Digraph.new
+ 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 configuration into the database.
+ def store(options)
+ 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(options)
+ end
+
+ # Do the actual storage.
+ def store_to_active_record(options)
+ begin
+ # We store all of the objects, even the collectable ones
+ benchmark(:info, "Stored configuration for #{options[:name]}") do
+ Puppet::Rails::Host.transaction do
+ Puppet::Rails::Host.store(options)
+ end
+ end
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ Puppet.err "Could not store configs: %s" % detail.to_s
+ end
end
- # Return the list of remaining overrides.
- def overrides
- @resource_overrides.values.flatten
+ # Add a tag.
+ def tag(*names)
+ names.each do |name|
+ name = name.to_s
+ @tags << name unless @tags.include?(name)
+ end
+ nil
+ end
+
+ # Return the list of tags.
+ def tags
+ @tags.dup
+ 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 = @resource_table.find_all do |name, object|
+ ! object.builtin? and ! object.evaluated?
+ end.collect { |name, object| object }
+
+ if ary.empty?
+ return nil
+ else
+ return ary
+ end
end
- def resources
- @resourcetable
+ # Verify that the given resource isn't defined elsewhere.
+ def verify_uniqueness(resource)
+ # Short-curcuit the common case,
+ unless existing_resource = @resource_table[resource.ref]
+ return true
+ end
+
+ if typeclass = Puppet::Type.type(resource.type) and ! typeclass.isomorphic?
+ Puppet.info "Allowing duplicate %s" % typeclass.name
+ return true
+ end
+
+ # Either it's a defined type, which are never
+ # isomorphic, or it's a non-isomorphic type, so
+ # we should throw an exception.
+ msg = "Duplicate definition: %s is already defined" % resource.ref
+
+ if existing_resource.file and existing_resource.line
+ msg << " in file %s at line %s" %
+ [existing_resource.file, existing_resource.line]
+ end
+
+ if resource.line or resource.file
+ msg << "; cannot redefine"
+ end
+
+ raise Puppet::ParseError.new(msg)
end
end
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 18bf31087..f0b9d0179 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -5,268 +5,26 @@ require 'puppet/util/methodhelper'
require 'puppet/parser/parser'
require 'puppet/parser/scope'
-# The interpreter's job is to convert from a parsed file to the configuration
-# for a given client. It really doesn't do any work on its own, it just collects
-# and calls out to other objects.
+# The interpreter is a very simple entry-point class that
+# manages the existence of the parser (e.g., replacing it
+# when files are reparsed). You can feed it a node and
+# get the node's configuration back.
class Puppet::Parser::Interpreter
- class NodeDef
- include Puppet::Util::MethodHelper
- attr_accessor :name, :classes, :parameters, :source
-
- def evaluate(options)
- begin
- parameters.each do |param, value|
- # Don't try to override facts with these parameters
- options[:scope].setvar(param, value) unless options[:scope].lookupvar(param, false) != :undefined
- end
-
- # Also, set the 'nodename', since it might not be obvious how the node was looked up
- options[:scope].setvar("nodename", @name) unless options[:scope].lookupvar(@nodename, false) != :undefined
- rescue => detail
- raise Puppet::ParseError, "Could not set parameters for %s: %s" % [name, detail]
- end
-
- # Then evaluate the classes.
- begin
- options[:scope].function_include(classes.find_all { |c| options[:scope].findclass(c) })
- rescue => detail
- puts detail.backtrace
- raise Puppet::ParseError, "Could not evaluate classes for %s: %s" % [name, detail]
- end
- end
-
- def initialize(args)
- set_options(args)
-
- raise Puppet::DevError, "NodeDefs require names" unless self.name
-
- if self.classes.is_a?(String)
- @classes = [@classes]
- else
- @classes ||= []
- end
- @parameters ||= {}
- end
-
- def safeevaluate(args)
- evaluate(args)
- end
- end
-
include Puppet::Util
attr_accessor :usenodes
- class << self
- attr_writer :ldap
- end
-
- # just shorten the constant path a bit, using what amounts to an alias
- AST = Puppet::Parser::AST
-
include Puppet::Util::Errors
- # Create an ldap connection. This is a class method so others can call
- # it and use the same variables and such.
- def self.ldap
- unless defined? @ldap and @ldap
- if Puppet[:ldapssl]
- @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
- elsif Puppet[:ldaptls]
- @ldap = LDAP::SSLConn.new(
- Puppet[:ldapserver], Puppet[:ldapport], true
- )
- else
- @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
- end
- @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
- @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
- @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
- end
-
- return @ldap
- 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 check_resource_collections(scope)
- remaining = []
- scope.collections.each do |coll|
- 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 find virtual resources %s" %
- remaining.join(', ')
- end
- end
-
- # Iteratively evaluate all of the objects. This finds all of the objects
- # that represent definitions and evaluates the definitions appropriately.
- # It also adds defaults and overrides as appropriate.
- def evaliterate(scope)
- count = 0
- loop do
- count += 1
- done = true
- # First perform collections, so we can collect defined types.
- if coll = scope.collections and ! coll.empty?
- exceptwrap do
- coll.each do |c|
- # Only keep the loop going if we actually successfully
- # collected something.
- if o = c.evaluate
- done = false
- end
- end
- end
- end
-
- # Then evaluate any defined types.
- if ary = scope.unevaluated
- ary.each do |resource|
- resource.evaluate
- end
- # If we evaluated, then loop through again.
- done = false
- end
- break if done
-
- if count > 1000
- raise Puppet::ParseError, "Got 1000 class levels, which is unsupported"
- end
- end
- end
-
- # Evaluate a specific node.
- def evalnode(client, scope, facts)
- return unless self.usenodes
-
- unless client
- raise Puppet::Error,
- "Cannot evaluate nodes with a nil client"
- end
- names = [client]
-
- # Make sure both the fqdn and the short name of the
- # host can be used in the manifest
- if client =~ /\./
- names << client.sub(/\..+/,'')
- else
- names << "#{client}.#{facts['domain']}"
- end
-
- if names.empty?
- raise Puppet::Error,
- "Cannot evaluate nodes with a nil client"
- end
-
- # Look up our node object.
- if nodeclass = nodesearch(*names)
- nodeclass.safeevaluate :scope => scope
- else
- raise Puppet::Error, "Could not find %s with names %s" %
- [client, names.join(", ")]
- end
- end
-
- # Evaluate all of the code we can find that's related to our client.
- def evaluate(client, facts)
- scope = Puppet::Parser::Scope.new(:interp => self) # no parent scope
- scope.name = "top"
- scope.type = "main"
-
- scope.host = client || facts["hostname"] || Facter.value(:hostname)
-
- classes = @classes.dup
-
- # Okay, first things first. Set our facts.
- scope.setfacts(facts)
-
- # Everyone will always evaluate the top-level class, if there is one.
- if klass = findclass("", "")
- # Set the source, so objects can tell where they were defined.
- scope.source = klass
- klass.safeevaluate :scope => scope, :nosubscope => true
- end
-
- # Next evaluate the node. We pass the facts so they can be used
- # when building the list of names for which to search.
- evalnode(client, scope, facts)
-
- # If we were passed any classes, evaluate those.
- if classes
- classes.each do |klass|
- if klassobj = findclass("", klass)
- klassobj.safeevaluate :scope => scope
- end
- end
- end
-
- # That was the first pass evaluation. Now iteratively evaluate
- # until we've gotten rid of all of everything or thrown an error.
- evaliterate(scope)
-
- # Now make sure we fail if there's anything left to do
- failonleftovers(scope)
-
- # Now finish everything. This recursively calls finish on the
- # contained scopes and resources.
- scope.finish
-
- # Store everything. We need to do this before translation, because
- # it operates on resources, not transobjects.
- if Puppet[:storeconfigs]
- args = {
- :resources => scope.resources,
- :name => scope.host,
- :facts => facts
- }
- unless scope.classlist.empty?
- args[:classes] = scope.classlist
- end
-
- storeconfigs(args)
- end
-
- # Now, finally, convert our scope tree + resources into a tree of
- # buckets and objects.
- objects = scope.translate
-
- # Add the class list
- unless scope.classlist.empty?
- objects.classes = scope.classlist
- end
-
- return objects
- end
-
- # Fail if there any overrides left to perform.
- def failonleftovers(scope)
- overrides = scope.overrides
- unless overrides.empty?
- fail Puppet::ParseError,
- "Could not find object(s) %s" % overrides.collect { |o|
- o.ref
- }.join(", ")
- end
-
- # Now check that there aren't any extra resource collections.
- check_resource_collections(scope)
- end
-
# Create proxy methods, so the scopes can call the interpreter, since
# they don't have access to the parser.
def findclass(namespace, name)
+ raise "move findclass() out of the interpreter"
@parser.findclass(namespace, name)
end
+
def finddefine(namespace, name)
+ raise "move finddefine() out of the interpreter"
@parser.finddefine(namespace, name)
end
@@ -284,33 +42,13 @@ class Puppet::Parser::Interpreter
@usenodes = true
end
-
- if Puppet[:ldapnodes]
- # Nodes in the file override nodes in ldap.
- @nodesource = :ldap
- elsif Puppet[:external_nodes] != "none"
- @nodesource = :external
- else
- # By default, we only search for parsed nodes.
- @nodesource = :code
- end
+ # By default, we only search for parsed nodes.
+ @nodesource = :code
@setup = false
- # Set it to either the value or nil. This is currently only used
- # by the cfengine module.
- @classes = hash[:Classes] || []
-
@local = hash[:Local] || false
- if hash.include?(:ForkSave)
- @forksave = hash[:ForkSave]
- else
- # This is just too dangerous right now. Sorry, it's going
- # to have to be slow.
- @forksave = false
- end
-
# The class won't always be defined during testing.
if Puppet[:storeconfigs]
if Puppet.features.rails?
@@ -329,6 +67,7 @@ class Puppet::Parser::Interpreter
# Pass these methods through to the parser.
[:newclass, :newdefine, :newnode].each do |name|
define_method(name) do |*args|
+ raise("move %s out of the interpreter" % name)
@parser.send(name, *args)
end
end
@@ -336,6 +75,7 @@ class Puppet::Parser::Interpreter
# Add a new file to be checked when we're checking to see if we should be
# reparsed.
def newfile(*files)
+ raise "who uses newfile?"
files.each do |file|
unless file.is_a? Puppet::Util::LoadedFile
file = Puppet::Util::LoadedFile.new(file)
@@ -344,90 +84,16 @@ class Puppet::Parser::Interpreter
end
end
- # Search for our node in the various locations.
- def nodesearch(*nodes)
- nodes = nodes.collect { |n| n.to_s.downcase }
-
- method = "nodesearch_%s" % @nodesource
- # Do an inverse sort on the length, so the longest match always
- # wins
- nodes.sort { |a,b| b.length <=> a.length }.each do |node|
- node = node.to_s if node.is_a?(Symbol)
- if obj = self.send(method, node)
- if obj.is_a?(AST::Node)
- nsource = obj.file
- else
- nsource = obj.source
- end
- Puppet.info "Found %s in %s" % [node, nsource]
- return obj
- end
- end
-
- # If they made it this far, we haven't found anything, so look for a
- # default node.
- unless nodes.include?("default")
- if defobj = self.nodesearch("default")
- Puppet.notice "Using default node for %s" % [nodes[0]]
- return defobj
- end
- end
-
- return nil
- end
-
- # See if our node was defined in the code.
- def nodesearch_code(name)
- @parser.nodes[name]
- end
-
def parsedate
parsefiles()
@parsedate
end
# evaluate our whole tree
- def run(client, facts)
- # We have to leave this for after initialization because there
- # seems to be a problem keeping ldap open after a fork.
- unless @setup
- method = "setup_%s" % @nodesource.to_s
- if respond_to? method
- exceptwrap :type => Puppet::Error,
- :message => "Could not set up node source %s" % @nodesource do
- self.send(method)
- end
- end
- end
+ def compile(node)
parsefiles()
- # Evaluate all of the appropriate code.
- objects = evaluate(client, facts)
-
- # And return it all.
- return objects
- end
-
- # Connect to the LDAP Server
- def setup_ldap
- self.class.ldap = nil
- unless Puppet.features.ldap?
- Puppet.notice(
- "Could not set up LDAP Connection: Missing ruby/ldap libraries"
- )
- @ldap = nil
- return
- end
-
- begin
- @ldap = self.class.ldap()
- rescue => detail
- raise Puppet::Error, "Could not connect to LDAP: %s" % detail
- end
- end
-
- def scope
- return @scope
+ return Puppet::Parser::Configuration.new(node).compile
end
private
@@ -504,47 +170,6 @@ class Puppet::Parser::Interpreter
Puppet.err "Could not parse; using old configuration: %s" % detail
end
end
-
- # Store the configs into the database.
- def storeconfigs(hash)
- unless Puppet.features.rails?
- raise Puppet::Error,
- "storeconfigs is enabled but rails is unavailable"
- end
-
- unless ActiveRecord::Base.connected?
- Puppet::Rails.connect
- end
-
- # Fork the storage, since we don't need the client waiting
- # on that. How do I avoid this duplication?
- if @forksave
- fork {
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{hash[:name]}") do
- # Try to batch things a bit, by putting them into
- # a transaction
- Puppet::Rails::Host.transaction do
- Puppet::Rails::Host.store(hash)
- end
- end
- }
- else
- begin
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{hash[:name]}") do
- Puppet::Rails::Host.transaction do
- Puppet::Rails::Host.store(hash)
- end
- end
- rescue => detail
- if Puppet[:trace]
- puts detail.backtrace
- end
- Puppet.err "Could not store configs: %s" % detail.to_s
- end
- end
- end
end
# $Id$
diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb
index 728f75a69..6d069dc07 100644
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@ -1,3 +1,5 @@
+# I pulled this into a separate file, because I got
+# tired of rebuilding the parser.rb file all the time.
class Puppet::Parser::Parser
require 'puppet/parser/functions'
diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb
index 18ec15ac0..371f56ec1 100644
--- a/lib/puppet/parser/resource.rb
+++ b/lib/puppet/parser/resource.rb
@@ -72,12 +72,15 @@ class Puppet::Parser::Resource
# Add any overrides for this object.
def addoverrides
- overrides = scope.lookupoverrides(self)
+ overrides = scope.configuration.resource_overrides(self)
+ raise "fix this test"
overrides.each do |over|
self.merge(over)
end
+ # Remove the overrides, so that the configuration knows there
+ # are none left.
overrides.clear
end
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 1fb4f6906..cb6d98584 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -56,84 +56,11 @@ class Puppet::Parser::Scope
end
end
- # Create a new child scope.
- def child=(scope)
- @children.push(scope)
-
- # Copy all of the shared tables over to the child.
- @@sharedtables.each do |name|
- scope.send(name.to_s + "=", self.send(name))
- end
- end
-
- # Verify that the given object isn't defined elsewhere.
- def chkobjectclosure(obj)
- if exobj = @definedtable[obj.ref]
- typeklass = Puppet::Type.type(obj.type)
- if typeklass and ! typeklass.isomorphic?
- Puppet.info "Allowing duplicate %s" % type
- else
- # Either it's a defined type, which are never
- # isomorphic, or it's a non-isomorphic type.
- msg = "Duplicate definition: %s is already defined" % obj.ref
-
- if exobj.file and exobj.line
- msg << " in file %s at line %s" %
- [exobj.file, exobj.line]
- end
-
- if obj.line or obj.file
- msg << "; cannot redefine"
- end
-
- raise Puppet::ParseError.new(msg)
- end
- end
-
- return true
- end
-
- # Remove a specific child.
- def delete(child)
- @children.delete(child)
- end
-
- # Remove a resource from the various tables. This is only used when
- # a resource maps to a definition and gets evaluated.
- def deleteresource(resource)
- if @definedtable[resource.ref]
- @definedtable.delete(resource.ref)
- end
-
- if @children.include?(resource)
- @children.delete(resource)
- end
- end
-
# Are we the top scope?
def topscope?
@level == 1
end
- # Yield each child scope in turn
- def each
- @children.each { |child|
- yield child
- }
- end
-
- # Evaluate a list of classes.
- def evalclasses(*classes)
- retval = []
- classes.each do |klass|
- if obj = findclass(klass)
- obj.safeevaluate :scope => self
- retval << klass
- end
- end
- retval
- end
-
def exported?
self.exported
end
@@ -157,25 +84,12 @@ class Puppet::Parser::Scope
end
def findresource(string, name = nil)
- if name
- string = "%s[%s]" % [string.capitalize, name]
- end
-
- @definedtable[string]
- end
-
- # Recursively complete the whole tree, in preparation for
- # translation or storage.
- def finish
- self.each do |obj|
- obj.finish
- end
+ configuration.findresource(string, name)
end
# Initialize our new scope. Defaults to having no parent and to
# being declarative.
def initialize(hash = {})
- @finished = false
if hash.include?(:namespace)
if n = hash[:namespace]
@namespaces = [n]
@@ -195,9 +109,6 @@ class Puppet::Parser::Scope
@tags = []
- # Our child scopes and objects
- @children = []
-
# The symbol table for this scope. This is where we store variables.
@symtable = {}
@@ -236,17 +147,6 @@ class Puppet::Parser::Scope
return values
end
- # Look up all of the exported objects of a given type.
- def lookupexported(type)
- @definedtable.find_all do |name, r|
- r.type == type and r.exported?
- end
- end
-
- def lookupoverrides(obj)
- @overridetable[obj.ref]
- end
-
# Look up a defined type.
def lookuptype(name)
finddefine(name) || findclass(name)
@@ -310,12 +210,6 @@ class Puppet::Parser::Scope
defined?(@nodescope) and @nodescope
end
- # Return the list of remaining overrides.
- def overrides
- #@overridetable.collect { |name, overs| overs }.flatten
- @overridetable.values.flatten
- end
-
# We probably shouldn't cache this value... But it's a lot faster
# than doing lots of queries.
def parent
@@ -359,38 +253,28 @@ class Puppet::Parser::Scope
nil
end
- # Set all of our facts in the top-level scope.
- def setfacts(facts)
- facts.each { |var, value|
- self.setvar(var, value)
- }
- end
-
# Add a new object to our object table and the global list, and do any necessary
# checks.
- def setresource(obj)
- self.chkobjectclosure(obj)
-
- @children << obj
+ def setresource(resource)
+ @configuration.store_resource(resource)
# Mark the resource as virtual or exported, as necessary.
if self.exported?
- obj.exported = true
+ resource.exported = true
elsif self.virtual?
- obj.virtual = true
+ resource.virtual = true
end
+ raise "setresource's tests aren't fixed"
- # The global table
- @definedtable[obj.ref] = obj
-
- return obj
+ return resource
end
# Override a parameter in an existing object. If the object does not yet
# exist, then cache the override in a global table, so it can be flushed
# at the end.
def setoverride(resource)
- resource.override = true
+ @configuration.store_override(resource)
+ raise "setoverride tests aren't fixed"
if obj = @definedtable[resource.ref]
obj.merge(resource)
else
@@ -560,18 +444,9 @@ class Puppet::Parser::Scope
end
end
- # Convert all of our objects as necessary.
- def translate
- ret = @children.collect do |child|
- case child
- when Puppet::Parser::Resource
- child.to_trans
- when self.class
- child.translate
- else
- devfail "Got %s for translation" % child.class
- end
- end.reject { |o| o.nil? }
+ # Convert our resource to a TransBucket.
+ def to_trans
+ raise "Scope#to_trans needs to be tested"
bucket = Puppet::TransBucket.new ret
case self.type