diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/defaults.rb | 2 | ||||
-rw-r--r-- | lib/puppet/indirector.rb | 2 | ||||
-rw-r--r-- | lib/puppet/indirector/exec.rb | 57 | ||||
-rw-r--r-- | lib/puppet/indirector/exec/node.rb | 50 | ||||
-rw-r--r-- | lib/puppet/indirector/indirection.rb | 5 | ||||
-rw-r--r-- | lib/puppet/indirector/ldap.rb | 90 | ||||
-rw-r--r-- | lib/puppet/indirector/ldap/node.rb | 75 | ||||
-rw-r--r-- | lib/puppet/indirector/node/external.rb | 89 | ||||
-rw-r--r-- | lib/puppet/indirector/node/ldap.rb | 140 | ||||
-rw-r--r-- | lib/puppet/indirector/null.rb | 9 | ||||
-rw-r--r-- | lib/puppet/indirector/null/node.rb (renamed from lib/puppet/indirector/node/none.rb) | 6 | ||||
-rw-r--r-- | lib/puppet/indirector/terminus.rb | 8 |
12 files changed, 297 insertions, 236 deletions
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index fa119bb5a..fa06c7fdb 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -554,7 +554,7 @@ module Puppet setdefaults(:parser, :typecheck => [true, "Whether to validate types during parsing."], :paramcheck => [true, "Whether to validate parameters during parsing."], - :node_source => ["none", "Where to look for node configuration information. + :node_terminus => ["null", "Where to look for node configuration information. The default node source, ``none``, just returns a node with its facts filled in, which is required for normal functionality. See the `NodeSourceReference`:trac: for more information."] diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index b89017c9a..bd2487e33 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -23,7 +23,7 @@ module Puppet::Indirector # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node) # & hook the instantiated Terminus into this class (Node: @indirection = terminus) - @indirection = Puppet::Indirector::Indirection.new(indirection) + @indirection = Puppet::Indirector::Indirection.new(self, indirection) end module ClassMethods diff --git a/lib/puppet/indirector/exec.rb b/lib/puppet/indirector/exec.rb new file mode 100644 index 000000000..7e4ac8d18 --- /dev/null +++ b/lib/puppet/indirector/exec.rb @@ -0,0 +1,57 @@ +require 'puppet/indirector/terminus' +require 'puppet/util' + +class Puppet::Indirector::Exec < Puppet::Indirector::Terminus + # Look for external node definitions. + def find(name) + # Run the command. + unless output = query(name) + return nil + end + + # Translate the output to ruby. + return output + end + + private + + # Proxy the execution, so it's easier to test. + def execute(command) + Puppet::Util.execute(command) + end + + # Call the external command and see if it returns our output. + def query(name) + external_command = command + + # Make sure it's an arry + unless external_command.is_a?(Array) + raise Puppet::DevError, "Exec commands must be an array" + end + + # Make sure it's fully qualified. + unless external_command[0][0] == File::SEPARATOR[0] + raise ArgumentError, "You must set the exec parameter to a fully qualified command" + end + + # Add our name to it. + external_command << name + begin + output = execute(external_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 exec %s terminus" % [name, self.name] + return nil + else + return output + end + end +end diff --git a/lib/puppet/indirector/exec/node.rb b/lib/puppet/indirector/exec/node.rb new file mode 100644 index 000000000..033afe3f0 --- /dev/null +++ b/lib/puppet/indirector/exec/node.rb @@ -0,0 +1,50 @@ +require 'puppet/indirector/exec' + +class Puppet::Indirector::Exec::Node < Puppet::Indirector::Exec + desc "Call an external program to get node information." + include Puppet::Util + + def command + command = Puppet[:external_nodes] + unless command != "none" + raise ArgumentError, "You must set the 'external_nodes' parameter to use the external node terminus" + end + command.split + end + + # Look for external node definitions. + def find(name) + output = super or return nil + + # Translate the output to ruby. + result = translate(name, output) + + return create_node(name, result) + end + + private + + # Turn our outputted objects into a Puppet::Node instance. + def create_node(name, result) + node = Puppet::Node.new(name) + set = false + [:parameters, :classes].each do |param| + if value = result[param] + node.send(param.to_s + "=", value) + set = true + end + end + + node.fact_merge + return node + end + + # Translate the yaml string into Ruby objects. + def translate(name, output) + begin + 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 + end +end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 31b9d68a5..12b8cf328 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -13,7 +13,7 @@ class Puppet::Indirector::Indirection @@indirections.find { |i| i.name == name } end - attr_accessor :name, :termini + attr_accessor :name, :model # Clear our cached list of termini. # This is only used for testing. @@ -26,7 +26,8 @@ class Puppet::Indirector::Indirection @@indirections.delete(self) if @@indirections.include?(self) end - def initialize(name, options = {}) + def initialize(model, name, options = {}) + @model = model @name = name options.each do |name, value| begin diff --git a/lib/puppet/indirector/ldap.rb b/lib/puppet/indirector/ldap.rb new file mode 100644 index 000000000..63848d10a --- /dev/null +++ b/lib/puppet/indirector/ldap.rb @@ -0,0 +1,90 @@ +require 'puppet/indirector/terminus' + +class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus + # Perform our ldap search and process the result. + def find(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(entry) } + + # Return nil if we haven't found something. + return nil + end + + # Process the found entry. We assume that we don't just want the + # ldap object. + def process(entry) + raise Puppet::DevError, "The 'process' method has not been overridden for the LDAP terminus for %s" % self.name + end + + # Default to all attributes. + def search_attributes + nil + end + + def search_base + Puppet[:ldapbase] + end + + # The ldap search filter to use. + def search_filter(name) + raise Puppet::DevError, "No search string set for LDAP terminus for %s" % self.name + end + + # Find the ldap node, return the class list and parent node specially, + # and everything else in a parameter hash. + def ldapsearch(node) + 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| + found = true + yield entry + end + rescue => detail + if count == 0 + # Try reconnecting to ldap if we get an exception and we haven't yet retried. + count += 1 + @connection = nil + Puppet.warning "Retrying LDAP connection" + retry + else + raise Puppet::Error, "LDAP Search failed: %s" % detail + end + end + + return found + end + + private + + # Create an ldap connection. + def connection + unless defined? @connection and @connection + unless Puppet.features.ldap? + raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries" + end + begin + if Puppet[:ldapssl] + @connection = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport]) + elsif Puppet[:ldaptls] + @connection = LDAP::SSLConn.new( + Puppet[:ldapserver], Puppet[:ldapport], true + ) + else + @connection = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport]) + end + @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) + @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) + @connection.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword]) + rescue => detail + raise Puppet::Error, "Could not connect to LDAP: %s" % detail + end + end + + return @connection + end +end diff --git a/lib/puppet/indirector/ldap/node.rb b/lib/puppet/indirector/ldap/node.rb new file mode 100644 index 000000000..187c74b94 --- /dev/null +++ b/lib/puppet/indirector/ldap/node.rb @@ -0,0 +1,75 @@ +require 'puppet/indirector/ldap' + +class Puppet::Indirector::Ldap::Node < Puppet::Indirector::Ldap + desc "Search in LDAP for node configuration information." + + # The attributes that Puppet class information is stored in. + def class_attributes + Puppet[:ldapclassattrs].split(/\s*,\s*/) + end + + # Look for our node in ldap. + def find(name) + unless ary = ldapsearch(name) + 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 + + node = Puppet::Node.new(name, :classes => classes, :source => "ldap", :parameters => parameters) + node.fact_merge + return node + end + + # The parent attribute, if we have one. + def parent_attribute + if pattr = Puppet[:ldapparentattr] and ! pattr.empty? + pattr + else + nil + end + end + + # Process the found entry. We assume that we don't just want the + # ldap object. + def process(entry) + raise Puppet::DevError, "The 'process' method has not been overridden for the LDAP terminus for %s" % self.name + end + + # Default to all attributes. + def search_attributes + ldapattrs = Puppet[:ldapattrs] + + # results in everything getting returned + return nil if ldapattrs == "all" + + search_attrs = class_attributes + ldapattrs.split(/\s*,\s*/) + + if pattr = parent_attribute + search_attrs << pattr + end + + search_attrs + end + + # The ldap search filter to use. + def search_filter(name) + filter = Puppet[:ldapstring] + + if filter.include? "%s" + # Don't replace the string in-line, since that would hard-code our node + # info. + filter = filter.gsub('%s', name) + end + filter + end +end diff --git a/lib/puppet/indirector/node/external.rb b/lib/puppet/indirector/node/external.rb deleted file mode 100644 index 66aca6048..000000000 --- a/lib/puppet/indirector/node/external.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'puppet/node/facts' - -Puppet::Indirector.register_terminus :node, :external do - desc "Call an external program to get node information." - - include Puppet::Util - - # Proxy the execution, so it's easier to test. - def execute(command) - Puppet::Util.execute(command) - end - - # Look for external node definitions. - def find(name) - unless Puppet[:external_nodes] != "none" - raise ArgumentError, "You must set the 'external_nodes' parameter to use the external node source" - end - - unless Puppet[:external_nodes][0] == File::SEPARATOR[0] - raise ArgumentError, "You must set the 'external_nodes' parameter to a fully qualified command" - end - - # Run the command. - unless output = query(name) - return nil - end - - # Translate the output to ruby. - result = translate(name, output) - - return create_node(name, result) - end - - private - - # Turn our outputted objects into a Puppet::Node instance. - def create_node(name, result) - node = Puppet::Node.new(name) - set = false - [:parameters, :classes].each do |param| - if value = result[param] - node.send(param.to_s + "=", value) - set = true - end - end - - if set - node.fact_merge - return node - else - return nil - end - end - - # Call the external command and see if it returns our output. - def query(name) - # 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 = 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 - else - return output - end - end - - # Translate the yaml string into Ruby objects. - def translate(name, output) - begin - 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 - end -end diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb deleted file mode 100644 index 230ca2467..000000000 --- a/lib/puppet/indirector/node/ldap.rb +++ /dev/null @@ -1,140 +0,0 @@ -Puppet::Indirector.register_terminus :node, :ldap do - desc "Search in LDAP for node configuration information." - - # Look for our node in ldap. - def find(name) - unless ary = ldapsearch(name) - 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 - - node = Puppet::Node.new(name, :classes => classes, :source => "ldap", :parameters => parameters) - node.fact_merge - return node - end - - # Find the ldap node, return the class list and parent node specially, - # and everything else in a parameter hash. - def ldapsearch(node) - 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 - 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 - - private - - # Create an ldap connection. - def ldap - unless defined? @ldap and @ldap - unless Puppet.features.ldap? - raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries" - end - begin - 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]) - rescue => detail - raise Puppet::Error, "Could not connect to LDAP: %s" % detail - end - end - - return @ldap - end -end diff --git a/lib/puppet/indirector/null.rb b/lib/puppet/indirector/null.rb new file mode 100644 index 000000000..db2b1db1c --- /dev/null +++ b/lib/puppet/indirector/null.rb @@ -0,0 +1,9 @@ +require 'puppet/indirector/terminus' + +# An empty terminus type, meant to just return empty objects. +class Puppet::Indirector::Null < Puppet::Indirector::Terminus + # Just return nothing. + def find(name) + indirection.model.new(name) + end +end diff --git a/lib/puppet/indirector/node/none.rb b/lib/puppet/indirector/null/node.rb index 034c0b349..eb08f5697 100644 --- a/lib/puppet/indirector/node/none.rb +++ b/lib/puppet/indirector/null/node.rb @@ -1,13 +1,13 @@ -require 'puppet/node/facts' +require 'puppet/indirector/null' -Puppet::Indirector.register_terminus :node, :none do +class Puppet::Indirector::Null::Node < Puppet::Indirector::Null desc "Always return an empty node object. This is the node source you should use when you don't have some other, functional source you want to use, as the compiler will not work without this node information." # Just return an empty node. def find(name) - node = Puppet::Node.new(name) + node = super node.fact_merge node end diff --git a/lib/puppet/indirector/terminus.rb b/lib/puppet/indirector/terminus.rb index f3797f471..bcff08d79 100644 --- a/lib/puppet/indirector/terminus.rb +++ b/lib/puppet/indirector/terminus.rb @@ -68,6 +68,10 @@ class Puppet::Indirector::Terminus @abstract_terminus = true end + def model + indirection.model + end + # Register a class, probably autoloaded. def register_terminus_class(klass) setup_instance_loading klass.terminus_type @@ -102,6 +106,10 @@ class Puppet::Indirector::Terminus def name self.class.name end + + def model + self.class.model + end def indirection self.class.indirection |