summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser/interpreter.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/parser/interpreter.rb')
-rw-r--r--lib/puppet/parser/interpreter.rb401
1 files changed, 13 insertions, 388 deletions
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$