summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-02-14 06:21:04 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-02-14 06:21:04 +0000
commit6cfee76f94824157a28354c9d6838716cb2c5d47 (patch)
treefb82fc83a94de987824aa129066df72878f3179c /lib/puppet/parser
parent19942633b69f04c6789ef08a04d434024e1bc334 (diff)
downloadpuppet-6cfee76f94824157a28354c9d6838716cb2c5d47.tar.gz
puppet-6cfee76f94824157a28354c9d6838716cb2c5d47.tar.xz
puppet-6cfee76f94824157a28354c9d6838716cb2c5d47.zip
Committing the initial ldap support -- puppet can now look up node configurations in ldap. The test scripts currently only work on my home network.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@909 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/parser')
-rw-r--r--lib/puppet/parser/ast/node.rb1
-rw-r--r--lib/puppet/parser/interpreter.rb125
-rw-r--r--lib/puppet/parser/scope.rb48
3 files changed, 168 insertions, 6 deletions
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index 2e33eb672..79fdd50d7 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -48,6 +48,7 @@ class Puppet::Parser::AST
# Evaluate our parent class.
def evalparent(scope)
if @parentclass
+ Puppet.warning "evaluating parent %s" % @parentclass
# This is pretty messed up. I don't know if this will
# work in the long term, but we need to evaluate the node
# in our own scope, even though our parent node has
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 7bb9f6b3b..ce6a9935c 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -10,6 +10,32 @@ require 'puppet/parser/scope'
module Puppet
module Parser
class Interpreter
+ Puppet.setdefaults("ldap",
+ [:ldapnodes, false,
+ "Whether to search for node configurations in LDAP."],
+ [:ldapserver, "ldap",
+ "The LDAP server. Only used if ``ldapnodes`` is enabled."],
+ [:ldapport, 389,
+ "The LDAP port. Only used if ``ldapnodes`` is enabled."],
+ [:ldapstring, "(&(objectclass=puppetClient)(cn=%s))",
+ "The search string used to find an LDAP node."],
+ [:ldapattrs, "puppetclass",
+ "The LDAP attributes to use to define Puppet classes. Values
+ should be comma-separated."],
+ [:ldapparentattr, "parentnode",
+ "The attribute to use to define the parent node."],
+ [:ldapuser, "",
+ "The user to use to connect to LDAP. Must be specified as a
+ full DN."],
+ [:ldappassword, "",
+ "The password to use to connect to LDAP."],
+ [:ldapbase, "",
+ "The search base for LDAP searches. It's impossible to provide
+ a meaningful default here, although the LDAP libraries might
+ have one already set. Generally, it should be the 'ou=Hosts'
+ branch under your main directory."]
+ )
+
attr_accessor :ast, :filetimeout
# just shorten the constant path a bit, using what amounts to an alias
AST = Puppet::Parser::AST
@@ -31,6 +57,20 @@ module Puppet
@usenodes = true
end
+ @nodesources = hash[:NodeSources] || [:file]
+
+ @nodesources.each { |source|
+ method = "setup_%s" % source.to_s
+ if respond_to? method
+ begin
+ self.send(method)
+ rescue => detail
+ raise Puppet::Error,
+ "Could not set up node source %s" % source
+ end
+ end
+ }
+
# Set it to either the value or nil. This is currently only used
# by the cfengine module.
@classes = hash[:Classes] || []
@@ -41,6 +81,73 @@ module Puppet
evaluate
end
+ # Connect to the LDAP Server
+ def setup_ldap
+ require 'ldap'
+ begin
+ @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+
+ # Find the ldap node and extra the info, returning just
+ # the critical data.
+ def nodesearch_ldap(node)
+ unless defined? @ldap
+ ldapconnect()
+ end
+
+ filter = Puppet[:ldapstring]
+ attrs = Puppet[:ldapattrs].split("\s*,\s*")
+ sattrs = attrs.dup
+ pattr = nil
+ if pattr = Puppet[:ldapparentattr]
+ if pattr == ""
+ pattr = nil
+ else
+ sattrs << pattr
+ end
+ end
+
+ if filter =~ /%s/
+ filter = filter.gsub(/%s/, node)
+ end
+
+ parent = nil
+ classes = []
+
+ found = false
+ # We're always doing a sub here; oh well.
+ @ldap.search(Puppet[:ldapbase], 2, filter, sattrs) 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
+
+ attrs.each { |attr|
+ if values = entry.vals(attr)
+ classes += values
+ end
+ }
+ end
+
+ classes.flatten!
+
+ return parent, classes
+ end
+
def parsedate
parsefiles()
@parsedate
@@ -67,8 +174,24 @@ module Puppet
"Cannot evaluate nodes with a nil client"
end
+ classes = nil
+ parent = nil
+ # At this point, stop at the first source that defines
+ # the node
+ @nodesources.each do |source|
+ method = "nodesearch_%s" % source
+ if self.respond_to? method
+ parent, classes = self.send(method, client)
+ end
+
+ if classes
+ Puppet.info "Found %s in %s" % [client, source]
+ break
+ end
+ end
+
# We've already evaluated the AST, in this case
- return @scope.evalnode(names, facts)
+ return @scope.evalnode(names, facts, classes, parent)
else
# We've already evaluated the AST, in this case
@scope = Puppet::Parser::Scope.new() # no parent scope
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 6cdc24178..27a64eec6 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -169,9 +169,16 @@ module Puppet
# 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
+ def evalnode(names, facts, classes = nil, parent = nil)
+ # First make sure there aren't any other node scopes lying around
+ self.nodeclean
+ # If they've passed classes in, then just generate from there.
+ if classes
+ return self.gennode(names, facts, classes, parent)
+ end
+
+ scope = code = nil
# Find a node that matches one of our names
names.each { |node|
if hash = @nodetable[node]
@@ -187,9 +194,6 @@ module Puppet
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
@@ -221,6 +225,40 @@ module Puppet
return objects
end
+ # Pull in all of the appropriate classes and evaluate them. It'd
+ # be nice if this didn't know quite so much about how AST::Node
+ # operated internally.
+ def gennode(names, facts, classes, parent)
+ name = names.shift
+ arghash = {
+ :name => name,
+ :code => AST::ASTArray.new(:pin => "[]")
+ }
+
+ if parent
+ arghash[:parentclass] = parent
+ end
+
+ # Create the node
+ node = AST::Node.new(arghash)
+ node.keyword = "node"
+ node.name = name
+
+ # Now evaluate it, which evaluates the parent but doesn't really
+ # do anything else but does return the nodescope
+ scope = node.safeevaluate(self)
+
+ # And now evaluate each set klass within the nodescope.
+ classes.each { |klass|
+ if code = scope.lookuptype(klass)
+ Puppet.warning "evaluating %s" % klass
+ code.safeevaluate(scope, {}, klass, klass)
+ end
+ }
+
+ return scope.to_trans
+ end
+
# Retrieve a specific node. This is used in ast.rb to find a
# parent node and in findnode to retrieve and evaluate a node.
def node(name)