diff options
author | Luke Kanies <luke@madstop.com> | 2008-07-01 21:55:53 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-07-01 22:22:07 -0500 |
commit | b47d4e1b3e1224541e555648854baf0503b1612e (patch) | |
tree | f6c49b41ce1a5b13fe58e715ca60e4caf15ee0ba /lib/puppet/indirector | |
parent | a1d1abdd5a2fc11dceeed63da8c6f48d2fa21cfe (diff) | |
download | puppet-b47d4e1b3e1224541e555648854baf0503b1612e.tar.gz puppet-b47d4e1b3e1224541e555648854baf0503b1612e.tar.xz puppet-b47d4e1b3e1224541e555648854baf0503b1612e.zip |
Added a 'search' method to the ldap node terminus.
This makes it easy to find multiple nodes in ldap, and
was done so it can be used by puppetrun.
Diffstat (limited to 'lib/puppet/indirector')
-rw-r--r-- | lib/puppet/indirector/ldap.rb | 19 | ||||
-rw-r--r-- | lib/puppet/indirector/node/ldap.rb | 152 |
2 files changed, 107 insertions, 64 deletions
diff --git a/lib/puppet/indirector/ldap.rb b/lib/puppet/indirector/ldap.rb index 695d38a95..7c3aca0da 100644 --- a/lib/puppet/indirector/ldap.rb +++ b/lib/puppet/indirector/ldap.rb @@ -1,21 +1,14 @@ require 'puppet/indirector/terminus' class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus - # We split this apart so it's easy to call multiple times with different names. - def entry2hash(name) - # We have to use 'yield' here because the LDAP::Entry objects - # get destroyed outside the scope of the search, strangely. - ldapsearch(name) { |entry| return process(name, entry) } - end - # Perform our ldap search and process the result. def find(request) - return entry2hash(request.key) || nil + return ldapsearch(search_filter(request.key)) { |entry| return process(entry) } || nil end # Process the found entry. We assume that we don't just want the # ldap object. - def process(name, entry) + def process(entry) raise Puppet::DevError, "The 'process' method has not been overridden for the LDAP terminus for %s" % self.name end @@ -35,14 +28,14 @@ class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus # Find the ldap node, return the class list and parent node specially, # and everything else in a parameter hash. - def ldapsearch(node) + def ldapsearch(filter) raise ArgumentError.new("You must pass a block to ldapsearch") unless block_given? found = false count = 0 begin - connection.search(search_base, 2, search_filter(node), search_attributes) do |entry| + connection.search(search_base, 2, filter, search_attributes) do |entry| found = true yield entry end @@ -54,7 +47,9 @@ class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus Puppet.warning "Retrying LDAP connection" retry else - raise Puppet::Error, "LDAP Search failed: %s" % detail + error = Puppet::Error.new("LDAP Search failed") + error.set_backtrace(detail.backtrace) + raise error end end diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb index 4ed053eff..2f953bbcb 100644 --- a/lib/puppet/indirector/node/ldap.rb +++ b/lib/puppet/indirector/node/ldap.rb @@ -13,6 +13,18 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap x = Puppet[:ldapclassattrs].split(/\s*,\s*/) end + # Separate this out so it's relatively atomic. It's tempting to call + # process() instead of entry2hash() here, but it ends up being + # difficult to test because all exceptions get caught by ldapsearch. + # LAK:NOTE Unfortunately, the ldap support is too stupid to throw anything + # but LDAP::ResultError, even on bad connections, so we are rough handed + # with our error handling. + def name2hash(name) + info = nil + ldapsearch(search_filter(name)) { |entry| info = entry2hash(entry) } + return info + end + # Look for our node in ldap. def find(request) names = [request.key] @@ -21,19 +33,40 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap end names << "default" - information = nil + node = nil names.each do |name| - break if information = entry2hash(name) + break if node = process(name) end - return nil unless information + return nil unless node - name = request.key + node.name = request.key - node = Puppet::Node.new(name) + return node + end - add_to_node(node, information) + def process(name) + return nil unless info = name2hash(name) - return node + info2node(name, info) + end + + # Find more than one node. LAK:NOTE This is a bit of a clumsy API, because the 'search' + # method currently *requires* a key. It seems appropriate in some cases but not others, + # and I don't really know how to get rid of it as a requirement but allow it when desired. + def search(key, options = {}) + if classes = options[:class] + classes = [classes] unless classes.is_a?(Array) + filter = "(&(objectclass=puppetClient)(puppetclass=" + classes.join(")(puppetclass=") + "))" + else + filter = "(objectclass=puppetClient)" + end + + infos = [] + ldapsearch(filter) { |entry| infos << entry2hash(entry) } + + return infos.collect do |info| + info2node(info[:name], info) + end end # The parent attribute, if we have one. @@ -51,15 +84,15 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap Puppet[:ldapstackedattrs].split(/\s*,\s*/) end - # Process the found entry. We assume that we don't just want the - # ldap object. - def process(name, entry) + # Convert the found entry into a simple hash. + def entry2hash(entry) result = {} + result[:name] = entry.dn.split(',')[0].split("=")[1] if pattr = parent_attribute if values = entry.vals(pattr) if values.length > 1 raise Puppet::Error, - "Node %s has more than one parent: %s" % [name, values.inspect] + "Node entry %s specifies more than one parent: %s" % [entry.dn, values.inspect] end unless values.empty? result[:parent] = values.shift @@ -73,9 +106,11 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap values.each do |v| result[:classes] << v end end } + result[:classes].uniq! result[:stacked] = [] - stacked_attributes.each { |attr| + stacked_params = stacked_attributes + stacked_params.each { |attr| if values = entry.vals(attr) result[:stacked] = result[:stacked] + values end @@ -83,17 +118,34 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap result[: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] + unless stacked_params.include?(ary[0]) # don't add our stacked parameters to the main param list + if ary[1].length == 1 + hash[ary[0]] = ary[1].shift + else + hash[ary[0]] = ary[1] + end end hash end result[:environment] = result[:parameters]["environment"] if result[:parameters]["environment"] - return result + result[:stacked_parameters] = {} + + if result[:stacked] + result[:stacked].each do |value| + param = value.split('=', 2) + result[:stacked_parameters][param[0]] = param[1] + end + end + + if result[:stacked_parameters] + result[:stacked_parameters].each do |param, value| + result[:parameters][param] = value unless result[:parameters].include?(param) + end + end + + result end # Default to all attributes. @@ -128,51 +180,17 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap # Add our hash of ldap information to the node instance. def add_to_node(node, information) - information[:stacked_parameters] = {} - - parent_info = nil - parent = information[:parent] - parents = [node.name] - while parent - if parents.include?(parent) - raise ArgumentError, "Found loop in LDAP node parents; %s appears twice" % parent - end - parents << parent - parent = find_and_merge_parent(parent, information) - end - - if information[:stacked] - information[:stacked].each do |value| - param = value.split('=', 2) - information[:stacked_parameters][param[0]] = param[1] - end - end - - if information[:stacked_parameters] - information[:stacked_parameters].each do |param, value| - information[:parameters][param] = value unless information[:parameters].include?(param) - end - end - node.classes = information[:classes].uniq unless information[:classes].nil? or information[:classes].empty? node.parameters = information[:parameters] unless information[:parameters].nil? or information[:parameters].empty? node.environment = information[:environment] if information[:environment] - node.fact_merge end # Find information for our parent and merge it into the current info. def find_and_merge_parent(parent, information) - parent_info = nil - ldapsearch(parent) { |entry| parent_info = process(parent, entry) } - - unless parent_info + unless parent_info = name2hash(parent) raise Puppet::Error.new("Could not find parent node '%s'" % parent) end information[:classes] += parent_info[:classes] - parent_info[:stacked].each do |value| - param = value.split('=', 2) - information[:stacked_parameters][param[0]] = param[1] - end parent_info[:parameters].each do |param, value| # Specifically test for whether it's set, so false values are handled # correctly. @@ -183,4 +201,34 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap parent_info[:parent] end + + # Take a name and a hash, and return a node instance. + def info2node(name, info) + merge_parent(info) if info[:parent] + + node = Puppet::Node.new(name) + + add_to_node(node, info) + + node.fact_merge + + node + end + + def merge_parent(info) + parent_info = nil + parent = info[:parent] + + # Preload the parent array with the node name. + parents = [info[:name]] + while parent + if parents.include?(parent) + raise ArgumentError, "Found loop in LDAP node parents; %s appears twice" % parent + end + parents << parent + parent = find_and_merge_parent(parent, info) + end + + return info + end end |