diff options
Diffstat (limited to 'lib/puppet/parser/interpreter.rb')
-rw-r--r-- | lib/puppet/parser/interpreter.rb | 401 |
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$ |