summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rwxr-xr-xlib/puppet/parsedfile.rb2
-rw-r--r--lib/puppet/parser/ast.rb21
-rw-r--r--lib/puppet/parser/grammar.ra33
-rw-r--r--lib/puppet/parser/interpreter.rb131
-rw-r--r--lib/puppet/parser/parser.rb37
-rw-r--r--lib/puppet/parser/scope.rb144
-rw-r--r--lib/puppet/server/master.rb76
-rw-r--r--lib/puppet/transportable.rb27
8 files changed, 259 insertions, 212 deletions
diff --git a/lib/puppet/parsedfile.rb b/lib/puppet/parsedfile.rb
index 337932309..e32a2daef 100755
--- a/lib/puppet/parsedfile.rb
+++ b/lib/puppet/parsedfile.rb
@@ -5,6 +5,8 @@ require 'puppet'
module Puppet
class ParsedFile
+ attr_reader :file
+
# Determine whether the file has changed and thus whether it should
# be reparsed
def changed?
diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
index a234bd165..897de71e9 100644
--- a/lib/puppet/parser/ast.rb
+++ b/lib/puppet/parser/ast.rb
@@ -1167,7 +1167,7 @@ module Puppet
names.each { |name|
begin
- scope.sethost(name,
+ scope.setnode(name,
Node.new(
:name => name,
:code => @code
@@ -1331,13 +1331,24 @@ module Puppet
class Node < AST::Component
attr_accessor :name, :args, :code, :parentclass
- def evaluate(scope,hash,objtype,objname)
+ def evaluate(scope, facts = {})
scope = scope.newscope
- scope.type = objtype
- scope.name = objname
+ scope.type = "node"
+ scope.name = @name
+
+ # Mark this scope as a nodescope, so that classes will be
+ # singletons within it
scope.nodescope = true
- self.code.safeevaluate(scope)
+ # Now set all of the facts inside this scope
+ facts.each { |var, value|
+ scope.setvar(var, value)
+ }
+
+ # And then evaluate our code.
+ @code.safeevaluate(scope)
+
+ return scope
end
end
#---------------------------------------------------------------
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index 7b05433ce..95ffa9772 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -586,6 +586,7 @@ endcomma: # nothing
end
---- header ----
require 'puppet'
+require 'puppet/parsedfile'
require 'puppet/parser/lexer'
require 'puppet/parser/ast'
#require 'puppet/parser/interpreter'
@@ -603,22 +604,26 @@ Puppet[:typecheck] = true
Puppet[:paramcheck] = true
---- inner ----
-attr_writer :stack
-attr_reader :file
+attr_reader :file, :files
def file=(file)
- if self.stack.include?(file)
+ unless FileTest.exists?(file)
+ raise Puppet::Error, "Could not find file %s" % file
+ end
+ if @files.detect { |f| f.file == file }
raise Puppet::ImportError.new("Import loop detected")
else
+ @files << Puppet::ParsedFile.new(file)
@lexer.file = file
end
end
def initialize
@lexer = Puppet::Parser::Lexer.new()
- if Puppet[:debug]
- @yydebut = true
- end
+ @files = []
+ #if Puppet[:debug]
+ # @yydebug = true
+ #end
end
def on_error(token,value,stack)
@@ -687,20 +692,8 @@ def parse
end
end
-def stack
- if defined? @stack and ! @stack.nil?
- if @lexer.file
- return [@stack,@lexer.file].flatten
- else
- return @stack
- end
- else
- if @lexer.file
- return [@lexer.file]
- else
- return []
- end
- end
+def reparse?
+ return @files.detect { |file| file.changed? }
end
def string=(string)
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 508bd17bd..9335d81aa 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -1,11 +1,6 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# the interpreter
-#
-# this builds our virtual pinball machine, into which we'll place our host-specific
-# information and out of which we'll receive our host-specific configuration
+# The interepreter'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.
require 'puppet'
require 'puppet/parser/parser'
@@ -14,66 +9,49 @@ require 'puppet/parser/scope'
module Puppet
module Parser
- #---------------------------------------------------------------
class Interpreter
attr_accessor :ast, :topscope
# just shorten the constant path a bit, using what amounts to an alias
AST = Puppet::Parser::AST
- #------------------------------------------------------------
- def clear
- TransObject.clear
- end
- #------------------------------------------------------------
-
- #------------------------------------------------------------
- #def callfunc(function,*args)
- # #Puppet.debug("Calling %s on %s" % [function,@client])
- # @client.callfunc(function,*args)
- # #Puppet.debug("Finished %s" % function)
- #end
- #------------------------------------------------------------
-
- #------------------------------------------------------------
# create our interpreter
def initialize(hash)
- unless hash.include?(:ast)
- raise ArgumentError.new("Must pass tree and client to Interpreter")
+ unless hash.include?(:Manifest)
+ raise Puppet::DevError, "Interpreter was not passed a file"
end
- @ast = hash[:ast]
- #@client = hash[:client]
- @scope = Puppet::Parser::Scope.new() # no parent scope
- @topscope = @scope
- @scope.interp = self
+ @file = hash[:Manifest]
- if hash.include?(:facts)
- facts = hash[:facts]
- unless facts.is_a?(Hash)
- raise ArgumentError.new("Facts must be a hash")
- end
-
- facts.each { |fact,value|
- @scope.setvar(fact,value)
- }
+ if hash.include?(:UseNodes)
+ @usenodes = hash[:UseNodes]
+ else
+ @usenodes = true
end
+
+ # Create our parser object
+ parsefiles
+
+ evaluate
end
- #------------------------------------------------------------
- #------------------------------------------------------------
# evaluate our whole tree
- def run
- # evaluate returns a value, but at the top level we only
- # care about its side effects
- # i think
- unless @ast.is_a?(AST) or @ast.is_a?(AST::ASTArray)
- Puppet.err "Received top-level non-ast '%s' of type %s" %
- [@ast,@ast.class]
- raise TypeError.new("Received non-ast '%s' of type %s" %
- [@ast,@ast.class])
- end
+ def run(client, facts)
+ parsefiles()
begin
+ if @usenodes
+ unless client
+ raise Puppet::Error,
+ "Cannot evaluate no nodes with a nil client"
+ end
+
+ # We've already evaluated the AST, in this case
+ @scope.evalnode(client, facts)
+ else
+ scope = Puppet::Parser::Scope.new() # no parent scope
+ scope.interp = self
+ scope.evaluate(@ast, facts)
+ end
@ast.evaluate(@scope)
rescue Puppet::DevError, Puppet::Error, Puppet::ParseError => except
#Puppet.err "File %s, line %s: %s" %
@@ -125,14 +103,55 @@ module Puppet
#TransObject.clear
return topbucket
end
- #------------------------------------------------------------
- #------------------------------------------------------------
def scope
return @scope
end
- #------------------------------------------------------------
+
+ private
+
+ # Evaluate the configuration. If there aren't any nodes defined, then
+ # this doesn't actually do anything, because we have to evaluate the
+ # entire configuration each time we get a connect.
+ def evaluate
+ @scope = Puppet::Parser::Scope.new() # no parent scope
+ @topscope = @scope
+ @scope.interp = self
+
+ if @usenodes
+ Puppet.debug "Nodes defined"
+ @ast.safeevaluate(@scope)
+ else
+ Puppet.debug "No nodes defined"
+ return
+ end
+ end
+
+ def parsefiles
+ if defined? @parser
+ unless @parser.reparse?
+ return false
+ end
+ end
+
+ unless FileTest.exists?(@file)
+ if @ast
+ Puppet.warning "Manifest %s has disappeared" % @file
+ return
+ else
+ raise Puppet::Error, "Manifest %s must exist" % @file
+ end
+ end
+
+ # should i be creating a new parser each time...?
+ @parser = Puppet::Parser::Parser.new()
+ @parser.file = @file
+ @ast = @parser.parse
+
+ evaluate
+ end
end
- #---------------------------------------------------------------
end
end
+
+# $Id$
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
index b636b7b37..6115dee4b 100644
--- a/lib/puppet/parser/parser.rb
+++ b/lib/puppet/parser/parser.rb
@@ -8,6 +8,7 @@ require 'racc/parser'
require 'puppet'
+require 'puppet/parsedfile'
require 'puppet/parser/lexer'
require 'puppet/parser/ast'
#require 'puppet/parser/interpreter'
@@ -31,23 +32,27 @@ module Puppet
class Parser < Racc::Parser
-module_eval <<'..end grammar.ra modeval..idea8fcb8472', 'grammar.ra', 606
-attr_writer :stack
-attr_reader :file
+module_eval <<'..end grammar.ra modeval..id859845cfb5', 'grammar.ra', 607
+attr_reader :file, :files
def file=(file)
- if self.stack.include?(file)
+ unless FileTest.exists?(file)
+ raise Puppet::Error, "Could not find file %s" % file
+ end
+ if @files.detect { |f| f.file == file }
raise Puppet::ImportError.new("Import loop detected")
else
+ @files << Puppet::ParsedFile.new(file)
@lexer.file = file
end
end
def initialize
@lexer = Puppet::Parser::Lexer.new()
- if Puppet[:debug]
- @yydebut = true
- end
+ @files = []
+ #if Puppet[:debug]
+ # @yydebug = true
+ #end
end
def on_error(token,value,stack)
@@ -116,26 +121,14 @@ def parse
end
end
-def stack
- if defined? @stack and ! @stack.nil?
- if @lexer.file
- return [@stack,@lexer.file].flatten
- else
- return @stack
- end
- else
- if @lexer.file
- return [@lexer.file]
- else
- return []
- end
- end
+def reparse?
+ return @files.detect { |file| file.changed? }
end
def string=(string)
@lexer.string = string
end
-..end grammar.ra modeval..idea8fcb8472
+..end grammar.ra modeval..id859845cfb5
##### racc 1.4.4 generates ###
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 37e5c39b7..56bf620dd 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -38,6 +38,30 @@ module Puppet
@@declarative
end
+ # Remove a specific child.
+ def delete(child)
+ @children.delete(child)
+ end
+
+ # Verify that no nodescopes are hanging around.
+ def nodeclean
+ @children.find_all { |child|
+ if child.is_a?(Scope)
+ child.nodescope?
+ else
+ false
+ end
+ }.each { |child|
+ @children.delete(child)
+ }
+
+ @children.each { |child|
+ if child.is_a?(Scope)
+ child.nodeclean
+ end
+ }
+ end
+
# Is this scope associated with being a node? The answer determines
# whether we store class instances here
def nodescope?
@@ -60,6 +84,65 @@ module Puppet
}
end
+ # Evaluate a specific node's code. This method will normally be called
+ # on the top-level scope, but it actually evaluates the node at the
+ # appropriate scope.
+ def evalnode(names, facts)
+ scope = code = nil
+ names.each { |node|
+ scope, code = self.findnode(node)
+ if scope and code
+ break
+ end
+ }
+
+ unless scope and code
+ raise Puppet::Error, "Could not find configuration for %s" %
+ names.join(" or ")
+ end
+
+ # First make sure there aren't any other node scopes lying around
+ self.nodeclean
+
+ # We need to do a little skullduggery here. We want a
+ # temporary scope, because we don't want this scope to
+ # show up permanently in the scope tree -- otherwise we could
+ # not evaluate the node multiple times. We could conceivably
+ # cache the results, but it's not worth it at this stage.
+
+ # Note that we evaluate the node code with its containing
+ # scope, not with the top scope.
+ code.safeevaluate(scope, facts)
+
+ # We don't need to worry about removing the Node code because
+ # it will be removed during translation.
+
+ # And now return the whole thing
+ return self.to_trans
+ end
+
+ # Find a given node's definition; searches downward recursively.
+ def findnode(node)
+ if @nodetable.include?(node)
+ return [self, @nodetable[node]]
+ else
+ self.find { |child|
+ child.findnode(node)
+ }
+ end
+ end
+
+ # Evaluate normally, with no node definitions
+ def evaluate(objects, facts = {})
+ facts.each { |var, value|
+ self.setvar(var, value)
+ }
+
+ objects.safeevaluate(self)
+
+ return self.to_trans
+ end
+
# Initialize our new scope. Defaults to having no parent and to
# being declarative.
def initialize(parent = nil, declarative = true)
@@ -88,6 +171,9 @@ module Puppet
# be used by top scopes and node scopes.
@classtable = Hash.new(nil)
+ # A table for storing nodes.
+ @nodetable = Hash.new(nil)
+
# All of the defaults set for types. It's a hash of hashes,
# with the first key being the type, then the second key being
# the parameter.
@@ -115,6 +201,7 @@ module Puppet
@map = {
"variable" => @symtable,
"type" => @typetable,
+ "node" => @nodetable,
"object" => @objectable,
"defaults" => @defaultstable
}
@@ -193,6 +280,18 @@ module Puppet
return values
end
+ # Look up a node by name
+ def lookupnode(name)
+ Puppet.debug "Looking up type %s" % name
+ value = self.lookup("type",name)
+ if value == :undefined
+ return nil
+ else
+ Puppet.debug "Found type %s" % name
+ return value
+ end
+ end
+
# Look up a defined type.
def lookuptype(name)
Puppet.debug "Looking up type %s" % name
@@ -209,18 +308,16 @@ module Puppet
def lookupobject(name,type)
Puppet.debug "Looking up object %s of type %s in level %s" %
[name, type, @level]
- unless defined? @@objectsearch
- @@objectsearch = proc { |table|
- if table.include?(type)
- if table[type].include?(name)
- table[type][name]
- end
- else
- nil
+ sub = proc { |table|
+ if table.include?(type)
+ if table[type].include?(name)
+ table[type][name]
end
- }
- end
- value = self.lookup("object",@@objectsearch)
+ else
+ nil
+ end
+ }
+ value = self.lookup("object",sub)
if value == :undefined
return nil
else
@@ -294,20 +391,14 @@ module Puppet
end
# Store a host in the global table.
- def sethost(name,host)
- if @@hosttable.include?(name)
- str = "Host %s is already defined" % name
- if @@hosttable[name].file
- str += " in file %s" % @@hosttable[name].file
- end
- if @@hosttable[name].line
- str += " on line %s" % @@hosttable[name].line
- end
- raise Puppet::ParseError,
- "Host %s is already defined" % name
+ def setnode(name,code)
+ if @nodetable.include?(name)
+ raise Puppet::Error, "Host %s is already defined" % name
else
- @@hosttable[name] = host
+ @nodetable[name] = code
end
+
+ #self.nodescope = true
end
# Define our type.
@@ -406,6 +497,13 @@ module Puppet
# Otherwise, just add it to our list of results.
results.push(cresult)
end
+
+ # Nodescopes are one-time; once they've been evaluated
+ # I need to destroy them. Nodeclean makes sure this is
+ # done correctly, but this should catch most of them.
+ if child.nodescope?
+ @children.delete(child)
+ end
elsif child.is_a?(TransObject)
results.push(child)
else
diff --git a/lib/puppet/server/master.rb b/lib/puppet/server/master.rb
index 099fe25c8..bf6998bac 100644
--- a/lib/puppet/server/master.rb
+++ b/lib/puppet/server/master.rb
@@ -22,11 +22,6 @@ class Server
@file = hash[:File] || Puppet[:manifest]
hash.delete(:File)
- @filestamp = nil
- @filestatted = nil
- @filetimeout = hash[:FileTimeout] || 60
- parsefile
-
if hash[:Local]
@local = hash[:Local]
else
@@ -38,13 +33,24 @@ class Server
else
@ca = nil
end
+
+ Puppet.debug("Creating interpreter")
+
+ args = {:Manifest => @file}
+
+ if @local
+ args[:UseNodes] = false
+ end
+
+ begin
+ @interpreter = Puppet::Parser::Interpreter.new(args)
+ rescue => detail
+ Puppet.err detail
+ raise
+ end
end
def getconfig(facts, client = nil, clientip = nil)
- parsefile()
- if client
- #Puppet.warning request.inspect
- end
if @local
# we don't need to do anything, since we should already
# have raw objects
@@ -62,20 +68,9 @@ class Server
end
end
- Puppet.debug("Creating interpreter")
-
- begin
- interpreter = Puppet::Parser::Interpreter.new(
- :ast => @ast,
- :facts => facts
- )
- rescue => detail
- return detail.to_s
- end
-
Puppet.debug("Running interpreter")
begin
- retobjects = interpreter.run()
+ retobjects = @interpreter.run(client, facts)
rescue => detail
Puppet.err detail.to_s
return ""
@@ -87,45 +82,6 @@ class Server
return CGI.escape(Marshal::dump(retobjects))
end
end
-
- private
-
- def parsefile
- if @filestamp and FileTest.exists?(@file)
- if @filetimeout and @filestatted
- if Time.now - @filestatted > @filetimeout
- tmp = File.stat(@file).ctime
-
- @filestatted = Time.now
- if tmp == @filestamp
- return
- else
- Puppet.notice "Reloading file"
- end
- else
- return
- end
- else
- @filestatted = Time.now
- end
- end
- @filestatted = Time.now
-
- unless FileTest.exists?(@file)
- if @ast
- Puppet.warning "Manifest %s has disappeared" % @file
- return
- else
- raise Puppet::Error, "Manifest %s must exist" % @file
- end
- end
- # should i be creating a new parser each time...?
- @parser = Puppet::Parser::Parser.new()
- @parser.file = @file
- @ast = @parser.parse
-
- @filestamp = File.stat(@file).ctime
- end
end
end
end
diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb
index 039a8d499..f50060d67 100644
--- a/lib/puppet/transportable.rb
+++ b/lib/puppet/transportable.rb
@@ -9,42 +9,17 @@ module Puppet
class TransObject < Hash
attr_accessor :type, :name, :file, :line
- @@ohash = {}
- @@oarray = []
-
- def TransObject.add(object)
- @@oarray.push object
-
- # this is just so we can check, at parse time, whether a required
- # object has already been mentioned when it is listed as required
- # because we're ordered, as long as an object gets made before its
- # dependent objects will get synced later
- @@ohash[object.longname] = object
- end
-
- def TransObject.clear
- @@oarray.clear
- end
-
- def TransObject.list
- return @@oarray
- end
-
def initialize(name,type)
self[:name] = name
@type = type
@name = name
- self.class.add(self)
+ #self.class.add(self)
end
def longname
return [self.type,self[:name]].join('--')
end
- #def name
- # return self[:name]
- #end
-
def to_s
return "%s(%s) => %s" % [@type,self[:name],super]
end