From aab419b8c1ad84e51c6f58839290bbe5d1e7b28b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 14 Aug 2007 00:09:49 -0500 Subject: An intermediate commit in the work towards adding multi-environment support. This has required splitting the interpreter up considerably, which is much cleaner but is a large project. There is now a 'nodes' handler, but it is currently non-functional, although all the support structure is there. It just needs to have the individual methods fleshed out, and it needs to be connected to the 'facts' handler. --- lib/puppet/network/handler/node.rb | 146 +++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 lib/puppet/network/handler/node.rb (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb new file mode 100644 index 000000000..0c532144a --- /dev/null +++ b/lib/puppet/network/handler/node.rb @@ -0,0 +1,146 @@ +# Created by Luke A. Kanies on 2007-08-13. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/util' +require 'puppet/util/classgen' +require 'puppet/util/instance_loader' + +# Look up a node, along with all the details about it. +class Puppet::Network::Handler::Node < Puppet::Network::Handler + # A simplistic class for managing the node information itself. + class SimpleNode + attr_accessor :name, :classes, :parameters, :environment + + def initialize(options) + unless @name = options[:name] + raise ArgumentError, "Nodes require names" unless self.name + end + + if classes = options[:classes] + if classes.is_a?(String) + @classes = [classes] + else + @classes = classes + end + else + @classes = [] + end + + @parameters = options[:parameters] || {} + end + end + desc "Retrieve information about nodes." + + extend Puppet::Util::ClassGen + extend Puppet::Util::InstanceLoader + + module SourceBase + include Puppet::Util::Docs + end + + @interface = XMLRPC::Service::Interface.new("nodes") { |iface| + iface.add_method("string node(key)") + iface.add_method("string parameters(key)") + iface.add_method("string environment(key)") + iface.add_method("string classes(key)") + } + + # Set up autoloading and retrieving of reports. + autoload :node_source, 'puppet/node_source' + + # Add a new node source. + def self.newnode_source(name, options = {}, &block) + name = symbolize(name) + + genmodule(name, :extend => SourceBase, :hash => instance_hash(:node_source), :block => block) + end + + # Collect the docs for all of our node sources. + def self.node_source_docs + docs = "" + + # Use this method so they all get loaded + instance_loader(:node_source).loadall + loaded_instances(:node_source).sort { |a,b| a.to_s <=> b.to_s }.each do |name| + mod = self.node_source(name) + docs += "%s\n%s\n" % [name, "-" * name.to_s.length] + + docs += Puppet::Util::Docs.scrub(mod.doc) + "\n\n" + end + + docs + end + + # List each of the node sources. + def self.node_sources + instance_loader(:node_source).loadall + loaded_instances(:node_source) + end + + # Remove a defined node source; basically only used for testing. + def self.rm_node_source(name) + instance_hash(:node_source).delete(name) + end + + # Return a given node's classes. + def classes(key) + raise "look up classes" + end + + # Return a given node's environment. + def environment(key) + raise "look up environment" + if node = node(key) + node.environment + else + nil + end + end + + # Return an entire node configuration. + def node(key) + # Try to find our node... + nodes = nodes.collect { |n| n.to_s.downcase } + + method = "nodesearch_%s" % @nodesource + # Do an inverse sort on the length, so the longest match always + # wins + nodes.sort { |a,b| b.length <=> a.length }.each do |node| + node = node.to_s if node.is_a?(Symbol) + if obj = self.send(method, node) + if obj.is_a?(AST::Node) + nsource = obj.file + else + nsource = obj.source + end + Puppet.info "Found %s in %s" % [node, nsource] + return obj + end + end + + # If they made it this far, we haven't found anything, so look for a + # default node. + unless nodes.include?("default") + if defobj = self.nodesearch("default") + Puppet.notice "Using default node for %s" % [nodes[0]] + return defobj + end + end + + return nil + end + + def parameters(key) + raise "Look up parameters" + end + + private + def node_facts(key) + raise "Look up node facts" + end + + def node_names(key, facts = nil) + facts ||= node_facts(key) + raise "Calculate node names" + end +end -- cgit From 90a9d09cd08ec072530e2f000e9f7b65f1c41095 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 14 Aug 2007 18:25:08 -0500 Subject: Finalizing the node handler. It now correctly uses the different node sources and knows how to retrieve data from those sources. Now I just need to fix the language stuff to use this handler instead of the existing node stuff. --- lib/puppet/network/handler/node.rb | 178 ++++++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 42 deletions(-) (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index 0c532144a..c9548f10b 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -9,12 +9,10 @@ require 'puppet/util/instance_loader' class Puppet::Network::Handler::Node < Puppet::Network::Handler # A simplistic class for managing the node information itself. class SimpleNode - attr_accessor :name, :classes, :parameters, :environment + attr_accessor :name, :classes, :parameters, :environment, :source - def initialize(options) - unless @name = options[:name] - raise ArgumentError, "Nodes require names" unless self.name - end + def initialize(name, options = {}) + @name = name if classes = options[:classes] if classes.is_a?(String) @@ -27,19 +25,35 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler end @parameters = options[:parameters] || {} + + unless @environment = options[:environment] + if env = Puppet[:environment] and env != "" + @environment = env + end + end + end + + # Merge the node facts with parameters from the node source. + # This is only called if the node source has 'fact_merge' set to true. + def fact_merge(facts) + facts.each do |name, value| + @parameters[name] = value unless @parameters.include?(name) + end end end + desc "Retrieve information about nodes." extend Puppet::Util::ClassGen extend Puppet::Util::InstanceLoader + # A simple base module we can use for modifying how our node sources work. module SourceBase include Puppet::Util::Docs end @interface = XMLRPC::Service::Interface.new("nodes") { |iface| - iface.add_method("string node(key)") + iface.add_method("string details(key)") iface.add_method("string parameters(key)") iface.add_method("string environment(key)") iface.add_method("string classes(key)") @@ -48,11 +62,18 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler # Set up autoloading and retrieving of reports. autoload :node_source, 'puppet/node_source' + attr_reader :source + # Add a new node source. def self.newnode_source(name, options = {}, &block) name = symbolize(name) - genmodule(name, :extend => SourceBase, :hash => instance_hash(:node_source), :block => block) + fact_merge = options[:fact_merge] + mod = genmodule(name, :extend => SourceBase, :hash => instance_hash(:node_source), :block => block) + mod.send(:define_method, :fact_merge?) do + fact_merge + end + mod end # Collect the docs for all of our node sources. @@ -79,68 +100,141 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler # Remove a defined node source; basically only used for testing. def self.rm_node_source(name) - instance_hash(:node_source).delete(name) + rmclass(name, :hash => instance_hash(:node_source)) end # Return a given node's classes. def classes(key) - raise "look up classes" - end - - # Return a given node's environment. - def environment(key) - raise "look up environment" - if node = node(key) - node.environment + if node = details(key) + node.classes else nil end end - # Return an entire node configuration. - def node(key) - # Try to find our node... - nodes = nodes.collect { |n| n.to_s.downcase } - - method = "nodesearch_%s" % @nodesource - # Do an inverse sort on the length, so the longest match always - # wins - nodes.sort { |a,b| b.length <=> a.length }.each do |node| - node = node.to_s if node.is_a?(Symbol) - if obj = self.send(method, node) - if obj.is_a?(AST::Node) - nsource = obj.file - else - nsource = obj.source - end - Puppet.info "Found %s in %s" % [node, nsource] - return obj + # Return an entire node configuration. This uses the 'nodesearch' method + # defined in the node_source to look for the node. + def details(key) + facts = node_facts(key) + node = nil + names = node_names(key, facts) + names.each do |name| + name = name.to_s if name.is_a?(Symbol) + if node = nodesearch(name) + Puppet.info "Found %s in %s" % [name, @source] + break end end # If they made it this far, we haven't found anything, so look for a # default node. - unless nodes.include?("default") - if defobj = self.nodesearch("default") - Puppet.notice "Using default node for %s" % [nodes[0]] - return defobj + unless node or names.include?("default") + if node = nodesearch("default") + Puppet.notice "Using default node for %s" % key + end + end + + if node + node.source = @source + + # Merge the facts into the parameters. + if fact_merge? + node.fact_merge(facts) end + return node + else + return nil + end + end + + # Return a given node's environment. + def environment(key) + if node = details(key) + node.environment + else + nil end + end + + # Create our node lookup tool. + def initialize(hash = {}) + @source = hash[:Source] || Puppet[:node_source] + + unless mod = self.class.node_source(@source) + raise ArgumentError, "Unknown node source '%s'" % @source + end + + extend(mod) - return nil + super end + # Short-hand for creating a new node, so the node sources don't need to + # specify the constant. + def newnode(options) + SimpleNode.new(options) + end + + # Try to retrieve a given node's parameters. def parameters(key) - raise "Look up parameters" + if node = details(key) + node.parameters + else + nil + end end private + + # Create/cache a fact handler. + def fact_handler + unless defined?(@fact_handler) + @fact_handler = Puppet::Network::Handler.handler(:facts).new + end + @fact_handler + end + + # Look up the node facts from our fact handler. def node_facts(key) - raise "Look up node facts" + if facts = fact_handler.get(key) + facts + else + {} + end end + # Calculate the list of node names we should use for looking + # up our node. def node_names(key, facts = nil) facts ||= node_facts(key) - raise "Calculate node names" + names = [] + + if hostname = facts["hostname"] + unless hostname == key + names << hostname + end + else + hostname = key + end + + if fqdn = facts["fqdn"] + hostname = fqdn + names << fqdn + end + + # Make sure both the fqdn and the short name of the + # host can be used in the manifest + if hostname =~ /\./ + names << hostname.sub(/\..+/,'') + elsif domain = facts['domain'] + names << hostname + "." + domain + end + + # Sort the names inversely by name length. + names.sort! { |a,b| b.length <=> a.length } + + # And make sure the key is first, since that's the most + # likely usage. + ([key] + names).uniq end end -- cgit From aabad8e1e262fb2f63fa4eef0f0e6fc00cc4b01f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 15 Aug 2007 14:21:59 -0500 Subject: Adding the necessary name/ip fields to the node methods --- lib/puppet/network/handler/node.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index c9548f10b..898db7c22 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -9,7 +9,7 @@ require 'puppet/util/instance_loader' class Puppet::Network::Handler::Node < Puppet::Network::Handler # A simplistic class for managing the node information itself. class SimpleNode - attr_accessor :name, :classes, :parameters, :environment, :source + attr_accessor :name, :classes, :parameters, :environment, :source, :ipaddress def initialize(name, options = {}) @name = name @@ -114,7 +114,7 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler # Return an entire node configuration. This uses the 'nodesearch' method # defined in the node_source to look for the node. - def details(key) + def details(key, client = nil, clientip = nil) facts = node_facts(key) node = nil names = node_names(key, facts) @@ -148,7 +148,7 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler end # Return a given node's environment. - def environment(key) + def environment(key, client = nil, clientip = nil) if node = details(key) node.environment else @@ -169,14 +169,8 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler super end - # Short-hand for creating a new node, so the node sources don't need to - # specify the constant. - def newnode(options) - SimpleNode.new(options) - end - # Try to retrieve a given node's parameters. - def parameters(key) + def parameters(key, client = nil, clientip = nil) if node = details(key) node.parameters else @@ -194,6 +188,12 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler @fact_handler end + # Short-hand for creating a new node, so the node sources don't need to + # specify the constant. + def newnode(options) + SimpleNode.new(options) + end + # Look up the node facts from our fact handler. def node_facts(key) if facts = fact_handler.get(key) -- cgit From a9539548d957304a03aa8bdc61c06793228f57c0 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 15 Aug 2007 17:05:49 -0500 Subject: Keeping the node names in the node object, so that they are available to the interpreter --- lib/puppet/network/handler/node.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index 898db7c22..cf39d630b 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -9,7 +9,7 @@ require 'puppet/util/instance_loader' class Puppet::Network::Handler::Node < Puppet::Network::Handler # A simplistic class for managing the node information itself. class SimpleNode - attr_accessor :name, :classes, :parameters, :environment, :source, :ipaddress + attr_accessor :name, :classes, :parameters, :environment, :source, :ipaddress, :names def initialize(name, options = {}) @name = name @@ -136,6 +136,7 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler if node node.source = @source + node.names = names # Merge the facts into the parameters. if fact_merge? -- cgit From 1527f4a615f9c429e90becd90f9ed1e8c1e83249 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 15 Aug 2007 17:18:43 -0500 Subject: Adding node caching, so that node sources are not spammed during file serving and such --- lib/puppet/network/handler/node.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index cf39d630b..0d685b1f1 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -10,6 +10,7 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler # A simplistic class for managing the node information itself. class SimpleNode attr_accessor :name, :classes, :parameters, :environment, :source, :ipaddress, :names + attr_reader :time def initialize(name, options = {}) @name = name @@ -31,6 +32,8 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler @environment = env end end + + @time = Time.now end # Merge the node facts with parameters from the node source. @@ -168,6 +171,9 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler extend(mod) super + + # We cache node info for speed + @node_cache = {} end # Try to retrieve a given node's parameters. @@ -181,6 +187,23 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler private + # Store the node to make things a bit faster. + def cache(node) + @node_cache[node.name] = node + end + + # If the node is cached, return it. + def cached?(name) + # Don't use cache when the filetimeout is set to 0 + return false if [0, "0"].include?(Puppet[:filetimeout]) + + if node = @node_cache[name] and Time.now - node.time < Puppet[:filetimeout] + return node + else + return false + end + end + # Create/cache a fact handler. def fact_handler unless defined?(@fact_handler) -- cgit From 4eb87ed7c8829a6fbc558595be9149e9b3cf5b36 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 20 Aug 2007 22:25:00 -0500 Subject: A round of bugfixing. Many more tests now pass -- I think we are largely down to tests that (yay!) fail in trunk. --- lib/puppet/network/handler/node.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index 0d685b1f1..a21d571b0 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -15,6 +15,9 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler def initialize(name, options = {}) @name = name + # Provide a default value. + @names = [name] + if classes = options[:classes] if classes.is_a?(String) @classes = [classes] -- cgit From 8b3361afae35cfb65754d7bd9aff5b820ed714f0 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 22 Aug 2007 17:57:28 -0500 Subject: The last commits before I actually start on the multi-environment support. There are still failing tests, but apparently only those that are also failing in trunk. --- lib/puppet/network/handler/node.rb | 84 ++++++++++---------------------------- 1 file changed, 22 insertions(+), 62 deletions(-) (limited to 'lib/puppet/network/handler/node.rb') diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index a21d571b0..2c4d3e1b5 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -2,74 +2,14 @@ # Copyright (c) 2007. All rights reserved. require 'puppet/util' +require 'puppet/node' require 'puppet/util/classgen' require 'puppet/util/instance_loader' # Look up a node, along with all the details about it. class Puppet::Network::Handler::Node < Puppet::Network::Handler - # A simplistic class for managing the node information itself. - class SimpleNode - attr_accessor :name, :classes, :parameters, :environment, :source, :ipaddress, :names - attr_reader :time - - def initialize(name, options = {}) - @name = name - - # Provide a default value. - @names = [name] - - if classes = options[:classes] - if classes.is_a?(String) - @classes = [classes] - else - @classes = classes - end - else - @classes = [] - end - - @parameters = options[:parameters] || {} - - unless @environment = options[:environment] - if env = Puppet[:environment] and env != "" - @environment = env - end - end - - @time = Time.now - end - - # Merge the node facts with parameters from the node source. - # This is only called if the node source has 'fact_merge' set to true. - def fact_merge(facts) - facts.each do |name, value| - @parameters[name] = value unless @parameters.include?(name) - end - end - end - desc "Retrieve information about nodes." - extend Puppet::Util::ClassGen - extend Puppet::Util::InstanceLoader - - # A simple base module we can use for modifying how our node sources work. - module SourceBase - include Puppet::Util::Docs - end - - @interface = XMLRPC::Service::Interface.new("nodes") { |iface| - iface.add_method("string details(key)") - iface.add_method("string parameters(key)") - iface.add_method("string environment(key)") - iface.add_method("string classes(key)") - } - - # Set up autoloading and retrieving of reports. - autoload :node_source, 'puppet/node_source' - - attr_reader :source - # Add a new node source. def self.newnode_source(name, options = {}, &block) name = symbolize(name) @@ -109,6 +49,26 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler rmclass(name, :hash => instance_hash(:node_source)) end + extend Puppet::Util::ClassGen + extend Puppet::Util::InstanceLoader + + # A simple base module we can use for modifying how our node sources work. + module SourceBase + include Puppet::Util::Docs + end + + @interface = XMLRPC::Service::Interface.new("nodes") { |iface| + iface.add_method("string details(key)") + iface.add_method("string parameters(key)") + iface.add_method("string environment(key)") + iface.add_method("string classes(key)") + } + + # Set up autoloading and retrieving of reports. + autoload :node_source, 'puppet/node_source' + + attr_reader :source + # Return a given node's classes. def classes(key) if node = details(key) @@ -218,7 +178,7 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler # Short-hand for creating a new node, so the node sources don't need to # specify the constant. def newnode(options) - SimpleNode.new(options) + Puppet::Node.new(options) end # Look up the node facts from our fact handler. -- cgit