summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser
diff options
context:
space:
mode:
authorJeffrey J McCune <jeff.mccune@northstarlabs.net>2007-08-15 15:52:27 -0400
committerJeffrey J McCune <jeff.mccune@northstarlabs.net>2007-08-15 15:52:27 -0400
commit7d09d4624dd1d4d69cf5acd355762503e9cb448c (patch)
tree1a306f28c07992195c56fe581ea5494d6d6c8971 /lib/puppet/parser
parentfb4ab97580f40d664e76aa7107e58d16097570b8 (diff)
parentaabad8e1e262fb2f63fa4eef0f0e6fc00cc4b01f (diff)
downloadpuppet-7d09d4624dd1d4d69cf5acd355762503e9cb448c.tar.gz
puppet-7d09d4624dd1d4d69cf5acd355762503e9cb448c.tar.xz
puppet-7d09d4624dd1d4d69cf5acd355762503e9cb448c.zip
Merge commit 'aabad8e'
Diffstat (limited to 'lib/puppet/parser')
-rw-r--r--lib/puppet/parser/ast/hostclass.rb7
-rw-r--r--lib/puppet/parser/configuration.rb133
-rw-r--r--lib/puppet/parser/interpreter.rb163
-rw-r--r--lib/puppet/parser/scope.rb222
4 files changed, 178 insertions, 347 deletions
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
index 642645824..d1ce370da 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -24,8 +24,11 @@ class Puppet::Parser::AST
scope = hash[:scope]
args = hash[:arguments]
- # Verify that we haven't already been evaluated
- if scope.class_scope(self)
+ # Verify that we haven't already been evaluated, and if we have been evaluated,
+ # make sure that we match the class.
+ if existing_scope = scope.class_scope(self)
+ raise "Fix this portion of the code -- check that the scopes match classes"
+ #if existing_scope.source.object_id == self.object_id
Puppet.debug "%s class already evaluated" % @type
return nil
end
diff --git a/lib/puppet/parser/configuration.rb b/lib/puppet/parser/configuration.rb
new file mode 100644
index 000000000..c7979e51f
--- /dev/null
+++ b/lib/puppet/parser/configuration.rb
@@ -0,0 +1,133 @@
+# Created by Luke A. Kanies on 2007-08-13.
+# Copyright (c) 2007. All rights reserved.
+
+require 'puppet/external/gratr/digraph'
+require 'puppet/external/gratr/import'
+require 'puppet/external/gratr/dot'
+
+# 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
+
+ # Add a collection to the global list.
+ def add_collection(coll)
+ @collections << coll
+ 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
+ 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 @class_scopes.keys.reject { |k| k == "" }
+ 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"
+
+ # Call the setup methods from the base class.
+ super()
+
+ initvars()
+ 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 = nil)
+ parent ||= @topscope
+ scope = Puppet::Parser::Scope.new(:configuration => self)
+ @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
+ 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 }
+
+ if ary.empty?
+ return nil
+ else
+ return ary
+ end
+ end
+
+ private
+
+ # 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 table for all defined resources.
+ @resource_table = {}
+
+ # 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 = []
+
+ # 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)
+ end
+
+ # Return the list of remaining overrides.
+ def overrides
+ @resource_overrides.values.flatten
+ end
+
+ def resources
+ @resourcetable
+ end
+end
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 3ba9c0c7a..18bf31087 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -178,7 +178,6 @@ class Puppet::Parser::Interpreter
# 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"
@@ -327,101 +326,6 @@ class Puppet::Parser::Interpreter
parsefiles
end
- # Find the ldap node, return the class list and parent node specially,
- # and everything else in a parameter hash.
- def ldapsearch(node)
- unless defined? @ldap and @ldap
- setup_ldap()
- unless @ldap
- Puppet.info "Skipping ldap source; no ldap connection"
- return nil
- end
- end
-
- filter = Puppet[:ldapstring]
- classattrs = Puppet[:ldapclassattrs].split("\s*,\s*")
- if Puppet[:ldapattrs] == "all"
- # A nil value here causes all attributes to be returned.
- search_attrs = nil
- else
- search_attrs = classattrs + Puppet[:ldapattrs].split("\s*,\s*")
- end
- pattr = nil
- if pattr = Puppet[:ldapparentattr]
- if pattr == ""
- pattr = nil
- else
- search_attrs << pattr unless search_attrs.nil?
- end
- end
-
- if filter =~ /%s/
- filter = filter.gsub(/%s/, node)
- end
-
- parent = nil
- classes = []
- parameters = nil
-
- found = false
- count = 0
-
- begin
- # We're always doing a sub here; oh well.
- @ldap.search(Puppet[:ldapbase], 2, filter, search_attrs) do |entry|
- found = true
- if pattr
- if values = entry.vals(pattr)
- if values.length > 1
- raise Puppet::Error,
- "Node %s has more than one parent: %s" %
- [node, values.inspect]
- end
- unless values.empty?
- parent = values.shift
- end
- end
- end
-
- classattrs.each { |attr|
- if values = entry.vals(attr)
- values.each do |v| classes << v end
- end
- }
-
- parameters = entry.to_hash.inject({}) do |hash, ary|
- if ary[1].length == 1
- hash[ary[0]] = ary[1].shift
- else
- hash[ary[0]] = ary[1]
- end
- hash
- end
- end
- rescue => detail
- if count == 0
- # Try reconnecting to ldap
- @ldap = nil
- setup_ldap()
- retry
- else
- raise Puppet::Error, "LDAP Search failed: %s" % detail
- end
- end
-
- classes.flatten!
-
- if classes.empty?
- classes = nil
- end
-
- if parent or classes or parameters
- return parent, classes, parameters
- else
- return nil
- end
- end
-
# Pass these methods through to the parser.
[:newclass, :newdefine, :newnode].each do |name|
define_method(name) do |*args|
@@ -476,73 +380,6 @@ class Puppet::Parser::Interpreter
def nodesearch_code(name)
@parser.nodes[name]
end
-
- # Look for external node definitions.
- def nodesearch_external(name)
- return nil unless Puppet[:external_nodes] != "none"
-
- # This is a very cheap way to do this, since it will break on
- # commands that have spaces in the arguments. But it's good
- # enough for most cases.
- external_node_command = Puppet[:external_nodes].split
- external_node_command << name
- begin
- output = Puppet::Util.execute(external_node_command)
- rescue Puppet::ExecutionFailure => detail
- if $?.exitstatus == 1
- return nil
- else
- Puppet.err "Could not retrieve external node information for %s: %s" % [name, detail]
- end
- return nil
- end
-
- if output =~ /\A\s*\Z/ # all whitespace
- Puppet.debug "Empty response for %s from external node source" % name
- return nil
- end
-
- begin
- result = YAML.load(output).inject({}) { |hash, data| hash[symbolize(data[0])] = data[1]; hash }
- rescue => detail
- raise Puppet::Error, "Could not load external node results for %s: %s" % [name, detail]
- end
-
- node_args = {:source => "external node source", :name => name}
- set = false
- [:parameters, :classes].each do |param|
- if value = result[param]
- node_args[param] = value
- set = true
- end
- end
-
- if set
- return NodeDef.new(node_args)
- else
- return nil
- end
- end
-
- # Look for our node in ldap.
- def nodesearch_ldap(node)
- unless ary = ldapsearch(node)
- return nil
- end
- parent, classes, parameters = ary
-
- while parent
- parent, tmpclasses, tmpparams = ldapsearch(parent)
- classes += tmpclasses if tmpclasses
- tmpparams.each do |param, value|
- # Specifically test for whether it's set, so false values are handled
- # correctly.
- parameters[param] = value unless parameters.include?(param)
- end
- end
-
- return NodeDef.new(:name => node, :classes => classes, :source => "ldap", :parameters => parameters)
- end
def parsedate
parsefiles()
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 6feeefc46..1fb4f6906 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -15,36 +15,18 @@ class Puppet::Parser::Scope
include Enumerable
include Puppet::Util::Errors
- attr_accessor :parent, :level, :interp, :source, :host
- attr_accessor :name, :type, :topscope, :base, :keyword
- attr_accessor :top, :translated, :exported, :virtual
+ attr_accessor :parent, :level, :interp, :source
+ attr_accessor :name, :type, :base, :keyword
+ attr_accessor :top, :translated, :exported, :virtual, :configuration
- # Whether we behave declaratively. Note that it's a class variable,
- # so all scopes behave the same.
- @@declarative = true
-
- # Retrieve and set the declarative setting.
- def self.declarative
- return @@declarative
- end
-
- def self.declarative=(val)
- @@declarative = val
+ # Proxy accessors
+ def host
+ @configuration.host
end
-
- # This handles the shared tables that all scopes have. They're effectively
- # global tables, except that they're only global for a single scope tree,
- # which is why I can't use class variables for them.
- def self.sharedtable(*names)
- attr_accessor(*names)
- @@sharedtables ||= []
- @@sharedtables += names
+ def interpreter
+ @configuration.interpreter
end
- # This is probably not all that good of an idea, but...
- # This way a parent can share its tables with all of its children.
- sharedtable :classtable, :definedtable, :exportable, :overridetable, :collecttable
-
# Is the value true? This allows us to control the definition of truth
# in one place.
def self.true?(value)
@@ -111,44 +93,6 @@ class Puppet::Parser::Scope
return true
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.
- def class_scope(klass)
- scope = if klass.respond_to?(:classname)
- @classtable[klass.classname]
- else
- @classtable[klass]
- end
-
- return nil unless scope
-
- if scope.nodescope? and ! klass.is_a?(AST::Node)
- raise Puppet::ParseError, "Node %s has already been evaluated; cannot evaluate class with same name" % [klass.classname]
- end
-
- scope
- end
-
- # Return the list of collections.
- def collections
- @collecttable
- end
-
- def declarative=(val)
- self.class.declarative = val
- end
-
- def declarative
- self.class.declarative
- end
-
- # Test whether a given scope is declarative. Even though it's
- # a global value, the calling objects don't need to know that.
- def declarative?
- @@declarative
- end
-
# Remove a specific child.
def delete(child)
@children.delete(child)
@@ -171,14 +115,6 @@ class Puppet::Parser::Scope
@level == 1
end
- # Return a list of all of the defined classes.
- def classlist
- unless defined? @classtable
- raise Puppet::DevError, "Scope did not receive class table"
- end
- return @classtable.keys.reject { |k| k == "" }
- end
-
# Yield each child scope in turn
def each
@children.each { |child|
@@ -239,9 +175,6 @@ class Puppet::Parser::Scope
# Initialize our new scope. Defaults to having no parent and to
# being declarative.
def initialize(hash = {})
- @parent = nil
- @type = nil
- @name = nil
@finished = false
if hash.include?(:namespace)
if n = hash[:namespace]
@@ -262,26 +195,6 @@ class Puppet::Parser::Scope
@tags = []
- if @parent.nil?
- unless hash.include?(:declarative)
- hash[:declarative] = true
- end
- self.istop(hash[:declarative])
- @inside = nil
- else
- # This is here, rather than in newchild(), so that all
- # of the later variable initialization works.
- @parent.child = self
-
- @level = @parent.level + 1
- @interp = @parent.interp
- @source = hash[:source] || @parent.source
- @topscope = @parent.topscope
- #@inside = @parent.inside # Used for definition inheritance
- @host = @parent.host
- @type ||= @parent.type
- end
-
# Our child scopes and objects
@children = []
@@ -294,62 +207,6 @@ class Puppet::Parser::Scope
@defaultstable = Hash.new { |dhash,type|
dhash[type] = {}
}
-
- unless @interp
- raise Puppet::DevError, "Scopes require an interpreter"
- end
- end
-
- # Associate the object directly with the scope, so that contained objects
- # can look up what container they're running within.
- def inside(arg = nil)
- return @inside unless arg
-
- old = @inside
- @inside = arg
- yield
- ensure
- #Puppet.warning "exiting %s" % @inside.name
- @inside = old
- end
-
- # Mark that we're the top scope, and set some hard-coded info.
- def istop(declarative = true)
- # the level is mostly used for debugging
- @level = 1
-
- # The table for storing class singletons. This will only actually
- # be used by top scopes and node scopes.
- @classtable = {}
-
- self.class.declarative = declarative
-
- # The table for all defined objects.
- @definedtable = {}
-
- # The list of objects that will available for export.
- @exportable = {}
-
- # The list of overrides. This is used to cache overrides on objects
- # that don't exist yet. We store an array of each override.
- @overridetable = Hash.new do |overs, ref|
- overs[ref] = []
- end
-
- # Eventually, if we support sites, this will allow definitions
- # of nodes with the same name in different sites. For now
- # the top-level scope is always the only site scope.
- @sitescope = true
-
- @namespaces = [""]
-
- # The list of collections that have been created. This is a global list,
- # but they each refer back to the scope that created them.
- @collecttable = []
-
- @topscope = self
- @type = "puppet"
- @name = "top"
end
# Collect all of the defaults set at any higher scopes.
@@ -360,8 +217,8 @@ class Puppet::Parser::Scope
values = {}
# first collect the values from the parents
- unless @parent.nil?
- @parent.lookupdefaults(type).each { |var,value|
+ unless parent.nil?
+ parent.lookupdefaults(type).each { |var,value|
values[var] = value
}
end
@@ -426,7 +283,7 @@ class Puppet::Parser::Scope
return @symtable[name]
end
elsif self.parent
- return @parent.lookupvar(name, usestring)
+ return parent.lookupvar(name, usestring)
elsif usestring
return ""
else
@@ -438,11 +295,6 @@ class Puppet::Parser::Scope
@namespaces.dup
end
- # Add a collection to the global list.
- def newcollection(coll)
- @collecttable << coll
- end
-
# Create a new scope.
def newscope(hash = {})
hash[:parent] = self
@@ -464,6 +316,25 @@ class Puppet::Parser::Scope
@overridetable.values.flatten
end
+ # We probably shouldn't cache this value... But it's a lot faster
+ # than doing lots of queries.
+ def parent
+ unless defined?(@parent)
+ @parent = configuration.parent(self)
+ end
+ @parent
+ end
+
+ # Return the list of scopes up to the top scope, ordered with our own first.
+ # This is used for looking up variables and defaults.
+ def scope_path
+ if parent
+ [self, parent.scope_path].flatten.compact
+ else
+ [self]
+ end
+ end
+
def resources
@definedtable.values
end
@@ -472,17 +343,17 @@ class Puppet::Parser::Scope
# that gets inherited from the top scope down, rather than a global
# hash. We store the object ID, not class name, so that we
# can support multiple unrelated classes with the same name.
- def setclass(obj)
- if obj.is_a?(AST::HostClass)
- unless obj.classname
+ def setclass(klass)
+ if klass.is_a?(AST::HostClass)
+ unless klass.classname
raise Puppet::DevError, "Got a %s with no fully qualified name" %
- obj.class
+ klass.class
end
- @classtable[obj.classname] = self
+ @configuration.class_set(klass.classname, self)
else
- raise Puppet::DevError, "Invalid class %s" % obj.inspect
+ raise Puppet::DevError, "Invalid class %s" % klass.inspect
end
- if obj.is_a?(AST::Node)
+ if klass.is_a?(AST::Node)
@nodescope = true
end
nil
@@ -665,9 +536,9 @@ class Puppet::Parser::Scope
unless ! defined? @type or @type.nil? or @type == ""
tmp << @type.to_s
end
- if @parent
- #info "Looking for tags in %s" % @parent.type
- @parent.tags.each { |tag|
+ if parent
+ #info "Looking for tags in %s" % parent.type
+ parent.tags.each { |tag|
if tag.nil? or tag == ""
Puppet.debug "parent returned tag %s" % tag.inspect
next
@@ -722,19 +593,6 @@ class Puppet::Parser::Scope
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 }
-
- if ary.empty?
- return nil
- else
- return ary
- end
- end
-
def virtual?
self.virtual || self.exported?
end