diff options
| author | Jeffrey J McCune <jeff.mccune@northstarlabs.net> | 2007-09-10 15:27:57 -0400 |
|---|---|---|
| committer | Jeffrey J McCune <jeff.mccune@northstarlabs.net> | 2007-09-10 15:27:57 -0400 |
| commit | e826453107fc3888da4667be9cce9cb7661072f8 (patch) | |
| tree | 92878fa77a20bd76b27ad34507f4f32274ee1ff2 /lib/puppet/network/handler | |
| parent | 09f7da5a5000876e03e9103c76089cfad9a1ba88 (diff) | |
| parent | 6700adcacdec4381ee4a27a215ee4b45207aa448 (diff) | |
Merge branch 'master' of git://reductivelabs.com/puppet
Diffstat (limited to 'lib/puppet/network/handler')
| -rw-r--r-- | lib/puppet/network/handler/configuration.rb | 304 | ||||
| -rwxr-xr-x | lib/puppet/network/handler/facts.rb | 2 | ||||
| -rwxr-xr-x | lib/puppet/network/handler/fileserver.rb | 42 | ||||
| -rw-r--r-- | lib/puppet/network/handler/master.rb | 209 | ||||
| -rw-r--r-- | lib/puppet/network/handler/node.rb | 110 | ||||
| -rwxr-xr-x | lib/puppet/network/handler/resource.rb | 4 |
6 files changed, 308 insertions, 363 deletions
diff --git a/lib/puppet/network/handler/configuration.rb b/lib/puppet/network/handler/configuration.rb index 7f81879ba..2c72d3d2b 100644 --- a/lib/puppet/network/handler/configuration.rb +++ b/lib/puppet/network/handler/configuration.rb @@ -8,219 +8,213 @@ require 'yaml' class Puppet::Network::Handler class Configuration < Handler desc "Puppet's configuration compilation interface. Passed a node name - or other key, retrieves information about the node and returns a - compiled configuration." + or other key, retrieves information about the node (using the ``node_source``) + and returns a compiled configuration." include Puppet::Util - attr_accessor :ast, :local - attr_reader :ca + attr_accessor :local @interface = XMLRPC::Service::Interface.new("configuration") { |iface| iface.add_method("string configuration(string)") iface.add_method("string version()") } - # FIXME At some point, this should be autodocumenting. - def addfacts(facts) - # Add our server version to the fact list - facts["serverversion"] = Puppet.version.to_s - - # And then add the server name and IP - {"servername" => "fqdn", - "serverip" => "ipaddress" - }.each do |var, fact| - if obj = Facter[fact] - facts[var] = obj.value - else - Puppet.warning "Could not retrieve fact %s" % fact - end + # Compile a node's configuration. + def configuration(key, client = nil, clientip = nil) + # If we want to use the cert name as our key + if Puppet[:node_name] == 'cert' and client + key = client end - if facts["servername"].nil? - host = Facter.value(:hostname) - if domain = Facter.value(:domain) - facts["servername"] = [host, domain].join(".") - else - facts["servername"] = host - end + # Note that this is reasonable, because either their node source should actually + # know about the node, or they should be using the ``none`` node source, which + # will always return data. + unless node = node_handler.details(key) + raise Puppet::Error, "Could not find node '%s'" % key end + + # Add any external data to the node. + add_node_data(node) + + configuration = compile(node) + + return translate(configuration) end - # Manipulate the client name as appropriate. - def clientname(name, ip, facts) - # Always use the hostname from Facter. - client = facts["hostname"] - clientip = facts["ipaddress"] - if Puppet[:node_name] == 'cert' - if name - client = name - end - if ip - clientip = ip - end + def initialize(options = {}) + if options[:Local] + @local = options[:Local] + else + @local = false end - return client, clientip + # Just store the options, rather than creating the interpreter + # immediately. Mostly, this is so we can create the interpreter + # on-demand, which is easier for testing. + @options = options + + set_server_facts end - # Tell a client whether there's a fresh config for it - def freshness(client = nil, clientip = nil) - if Puppet.features.rails? and Puppet[:storeconfigs] - Puppet::Rails.connect + # Are we running locally, or are our clients networked? + def local? + self.local + end - host = Puppet::Rails::Host.find_or_create_by_name(client) - host.last_freshcheck = Time.now - if clientip and (! host.ip or host.ip == "" or host.ip == "NULL") - host.ip = clientip - end - host.save - end - if defined? @interpreter - return @interpreter.parsedate + # Return the configuration version. + def version(client = nil, clientip = nil) + if client and node = node_handler.details(client) + update_node_check(node) + return interpreter.configuration_version(node) else - return 0 + # Just return something that will always result in a recompile, because + # this is local. + return (Time.now + 1000).to_i end end - def initialize(hash = {}) - args = {} + private - # Allow specification of a code snippet or of a file - if code = hash[:Code] - args[:Code] = code - else - args[:Manifest] = hash[:Manifest] || Puppet[:manifest] + # Add any extra data necessary to the node. + def add_node_data(node) + # Merge in our server-side facts, so they can be used during compilation. + node.fact_merge(@server_facts) + + # Add any specified classes to the node's class list. + if classes = @options[:Classes] + classes.each do |klass| + node.classes << klass + end end + end - if hash[:Local] - @local = hash[:Local] + # Compile the actual configuration. + def compile(node) + # Pick the benchmark level. + if local? + level = :none else - @local = false + level = :notice end - args[:Local] = @local + # Ask the interpreter to compile the configuration. + str = "Compiled configuration for %s" % node.name + if node.environment + str += " in environment %s" % node.environment + end + config = nil + benchmark(level, "Compiled configuration for %s" % node.name) do + begin + config = interpreter.compile(node) + rescue Puppet::Error => detail + if Puppet[:trace] + puts detail.backtrace + end + unless local? + Puppet.err detail.to_s + end + raise XMLRPC::FaultException.new( + 1, detail.to_s + ) + end + end - if hash.include?(:CA) and hash[:CA] - @ca = Puppet::SSLCertificates::CA.new() - else - @ca = nil + return config + end + + # Create our interpreter object. + def create_interpreter(options) + args = {} + + # Allow specification of a code snippet or of a file + if code = options[:Code] + args[:Code] = code + elsif options[:Manifest] + args[:Manifest] = options[:Manifest] end - Puppet.debug("Creating interpreter") + args[:Local] = local? - if hash.include?(:UseNodes) - args[:UseNodes] = hash[:UseNodes] + if options.include?(:UseNodes) + args[:UseNodes] = options[:UseNodes] elsif @local args[:UseNodes] = false end # This is only used by the cfengine module, or if --loadclasses was # specified in +puppet+. - if hash.include?(:Classes) - args[:Classes] = hash[:Classes] + if options.include?(:Classes) + args[:Classes] = options[:Classes] end - @interpreter = Puppet::Parser::Interpreter.new(args) + return Puppet::Parser::Interpreter.new(args) end - def getconfig(facts, format = "marshal", client = nil, clientip = nil) - if @local - # we don't need to do anything, since we should already - # have raw objects - Puppet.debug "Our client is local" - else - Puppet.debug "Our client is remote" - - # XXX this should definitely be done in the protocol, somehow - case format - when "marshal": - Puppet.warning "You should upgrade your client. 'Marshal' will not be supported much longer." - begin - facts = Marshal::load(CGI.unescape(facts)) - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not rebuild facts" - ) - end - when "yaml": - begin - facts = YAML.load(CGI.unescape(facts)) - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not rebuild facts" - ) - end - else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) - end + # Create/return our interpreter. + def interpreter + unless defined?(@interpreter) and @interpreter + @interpreter = create_interpreter(@options) end + @interpreter + end - client, clientip = clientname(client, clientip, facts) + # Create a node handler instance for looking up our nodes. + def node_handler + unless defined?(@node_handler) + @node_handler = Puppet::Network::Handler.handler(:node).create + end + @node_handler + end - # Add any server-side facts to our server. - addfacts(facts) + # Initialize our server fact hash; we add these to each client, and they + # won't change while we're running, so it's safe to cache the values. + def set_server_facts + @server_facts = {} - retobjects = nil + # Add our server version to the fact list + @server_facts["serverversion"] = Puppet.version.to_s - # This is hackish, but there's no "silence" option for benchmarks - # right now - if @local - #begin - retobjects = @interpreter.run(client, facts) - #rescue Puppet::Error => detail - # Puppet.err detail - # raise XMLRPC::FaultException.new( - # 1, detail.to_s - # ) - #rescue => detail - # Puppet.err detail.to_s - # return "" - #end - else - benchmark(:notice, "Compiled configuration for %s" % client) do - begin - retobjects = @interpreter.run(client, facts) - rescue Puppet::Error => detail - Puppet.err detail - raise XMLRPC::FaultException.new( - 1, detail.to_s - ) - rescue => detail - Puppet.err detail.to_s - return "" - end + # And then add the server name and IP + {"servername" => "fqdn", + "serverip" => "ipaddress" + }.each do |var, fact| + if value = Facter.value(fact) + @server_facts[var] = value + else + Puppet.warning "Could not retrieve fact %s" % fact end end - if @local - return retobjects - else - str = nil - case format - when "marshal": - str = Marshal::dump(retobjects) - when "yaml": - str = retobjects.to_yaml(:UseBlock => true) + if @server_facts["servername"].nil? + host = Facter.value(:hostname) + if domain = Facter.value(:domain) + @server_facts["servername"] = [host, domain].join(".") else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) + @server_facts["servername"] = host end - return CGI.escape(str) end end - def local? - if defined? @local and @local - return true + # Translate our configuration appropriately for sending back to a client. + def translate(config) + if local? + config else - return false + CGI.escape(config.to_yaml(:UseBlock => true)) + end + end + + # Mark that the node has checked in. FIXME this needs to be moved into + # the Node class, or somewhere that's got abstract backends. + def update_node_check(node) + if Puppet.features.rails? and Puppet[:storeconfigs] + Puppet::Rails.connect + + host = Puppet::Rails::Host.find_or_create_by_name(node.name) + host.last_freshcheck = Time.now + host.save end end end end - -# $Id$ diff --git a/lib/puppet/network/handler/facts.rb b/lib/puppet/network/handler/facts.rb index e0b93f942..4767e8be4 100755 --- a/lib/puppet/network/handler/facts.rb +++ b/lib/puppet/network/handler/facts.rb @@ -66,5 +66,3 @@ class Puppet::Network::Handler end end end - -# $Id$ diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb index fdf515d6e..a429412d2 100755 --- a/lib/puppet/network/handler/fileserver.rb +++ b/lib/puppet/network/handler/fileserver.rb @@ -239,6 +239,33 @@ class Puppet::Network::Handler return children end + # Return the mount for the Puppet modules; allows file copying from + # the modules. + def modules_mount(module_name, client) + # Find our environment, if we have one. + if node = node_handler.details(client || Facter.value("hostname")) + env = node.environment + else + env = nil + end + + # And use the environment to look up the module. + mod = Puppet::Module::find(module_name, env) + if mod + return @mounts[MODULES].copy(mod.name, mod.files) + else + return nil + end + end + + # Create a node handler instance for looking up our nodes. + def node_handler + unless defined?(@node_handler) + @node_handler = Puppet::Network::Handler.handler(:node).create + end + @node_handler + end + # Read the configuration file. def readconfig(check = true) return if @noreadconfig @@ -388,15 +415,12 @@ class Puppet::Network::Handler mount = nil path = nil if dir =~ %r{/([-\w]+)/?} - tmp = $1 - path = dir.sub(%r{/#{tmp}/?}, '') + # Strip off the mount name. + mount_name, path = dir.sub(%r{^/}, '').split(File::Separator, 2) - mod = Puppet::Module::find(tmp) - if mod - mount = @mounts[MODULES].copy(mod.name, mod.files) - else - unless mount = @mounts[tmp] - raise FileServerError, "Fileserver module '%s' not mounted" % tmp + unless mount = modules_mount(mount_name, client) + unless mount = @mounts[mount_name] + raise FileServerError, "Fileserver module '%s' not mounted" % mount_name end end else @@ -405,7 +429,7 @@ class Puppet::Network::Handler if path == "" path = nil - else + elsif path # Remove any double slashes that might have occurred path = URI.unescape(path.gsub(/\/\//, "/")) end diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index e889c1ba8..c8db277ba 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -13,7 +13,7 @@ class Puppet::Network::Handler include Puppet::Util - attr_accessor :ast, :local + attr_accessor :ast attr_reader :ca @interface = XMLRPC::Service::Interface.new("puppetmaster") { |iface| @@ -21,66 +21,10 @@ class Puppet::Network::Handler iface.add_method("int freshness()") } - # FIXME At some point, this should be autodocumenting. - def addfacts(facts) - # Add our server version to the fact list - facts["serverversion"] = Puppet.version.to_s - - # And then add the server name and IP - {"servername" => "fqdn", - "serverip" => "ipaddress" - }.each do |var, fact| - if obj = Facter[fact] - facts[var] = obj.value - else - Puppet.warning "Could not retrieve fact %s" % fact - end - end - - if facts["servername"].nil? - host = Facter.value(:hostname) - if domain = Facter.value(:domain) - facts["servername"] = [host, domain].join(".") - else - facts["servername"] = host - end - end - end - - # Manipulate the client name as appropriate. - def clientname(name, ip, facts) - # Always use the hostname from Facter. - client = facts["hostname"] - clientip = facts["ipaddress"] - if Puppet[:node_name] == 'cert' - if name - client = name - end - if ip - clientip = ip - end - end - - return client, clientip - end - # Tell a client whether there's a fresh config for it def freshness(client = nil, clientip = nil) - if Puppet.features.rails? and Puppet[:storeconfigs] - Puppet::Rails.connect - - host = Puppet::Rails::Host.find_or_create_by_name(client) - host.last_freshcheck = Time.now - if clientip and (! host.ip or host.ip == "" or host.ip == "NULL") - host.ip = clientip - end - host.save - end - if defined? @interpreter - return @interpreter.parsedate - else - return 0 - end + client ||= Facter.value("hostname") + config_handler.version(client, clientip) end def initialize(hash = {}) @@ -89,8 +33,8 @@ class Puppet::Network::Handler # Allow specification of a code snippet or of a file if code = hash[:Code] args[:Code] = code - else - args[:Manifest] = hash[:Manifest] || Puppet[:manifest] + elsif man = hash[:Manifest] + args[:Manifest] = man end if hash[:Local] @@ -99,7 +43,7 @@ class Puppet::Network::Handler @local = false end - args[:Local] = @local + args[:Local] = true if hash.include?(:CA) and hash[:CA] @ca = Puppet::SSLCertificates::CA.new() @@ -121,106 +65,89 @@ class Puppet::Network::Handler args[:Classes] = hash[:Classes] end - @interpreter = Puppet::Parser::Interpreter.new(args) + @config_handler = Puppet::Network::Handler.handler(:configuration).new(args) end + # Call our various handlers; this handler is getting deprecated. def getconfig(facts, format = "marshal", client = nil, clientip = nil) - if @local - # we don't need to do anything, since we should already - # have raw objects - Puppet.debug "Our client is local" - else - Puppet.debug "Our client is remote" + facts = decode_facts(facts) + client, clientip = clientname(client, clientip, facts) - # XXX this should definitely be done in the protocol, somehow - case format - when "marshal": - Puppet.warning "You should upgrade your client. 'Marshal' will not be supported much longer." - begin - facts = Marshal::load(CGI.unescape(facts)) - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not rebuild facts" - ) - end - when "yaml": - begin - facts = YAML.load(CGI.unescape(facts)) - rescue => detail - raise XMLRPC::FaultException.new( - 1, "Could not rebuild facts" - ) - end - else - raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format - ) - end - end + # Pass the facts to the fact handler + fact_handler.set(client, facts) - client, clientip = clientname(client, clientip, facts) + # And get the configuration from the config handler + begin + config = config_handler.configuration(client) + rescue => detail + puts detail.backtrace + raise + end - # Add any server-side facts to our server. - addfacts(facts) + return translate(config.extract) + end - retobjects = nil + private - # This is hackish, but there's no "silence" option for benchmarks - # right now - if @local - #begin - retobjects = @interpreter.run(client, facts) - #rescue Puppet::Error => detail - # Puppet.err detail - # raise XMLRPC::FaultException.new( - # 1, detail.to_s - # ) - #rescue => detail - # Puppet.err detail.to_s - # return "" - #end - else - benchmark(:notice, "Compiled configuration for %s" % client) do - begin - retobjects = @interpreter.run(client, facts) - rescue Puppet::Error => detail - Puppet.err detail - raise XMLRPC::FaultException.new( - 1, detail.to_s - ) - rescue => detail - Puppet.err detail.to_s - return "" - end + # Manipulate the client name as appropriate. + def clientname(name, ip, facts) + # Always use the hostname from Facter. + client = facts["hostname"] + clientip = facts["ipaddress"] + if Puppet[:node_name] == 'cert' + if name + client = name + end + if ip + clientip = ip end end + return client, clientip + end + + def config_handler + unless defined? @config_handler + @config_handler = Puppet::Network::Handler.handler(:config).new :local => local? + end + @config_handler + end + + # + def decode_facts(facts) if @local - return retobjects + # we don't need to do anything, since we should already + # have raw objects + Puppet.debug "Our client is local" else - str = nil - case format - when "marshal": - str = Marshal::dump(retobjects) - when "yaml": - str = retobjects.to_yaml(:UseBlock => true) - else + Puppet.debug "Our client is remote" + + begin + facts = YAML.load(CGI.unescape(facts)) + rescue => detail raise XMLRPC::FaultException.new( - 1, "Unavailable config format %s" % format + 1, "Could not rebuild facts" ) end - return CGI.escape(str) end + + return facts end - def local? - if defined? @local and @local - return true + def fact_handler + unless defined? @fact_handler + @fact_handler = Puppet::Network::Handler.handler(:facts).new :local => local? + end + @fact_handler + end + + # Translate our configuration appropriately for sending back to a client. + def translate(config) + if local? + config else - return false + CGI.escape(config.to_yaml(:UseBlock => true)) end end end end - -# $Id$ diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb index 898db7c22..bf2424b18 100644 --- a/lib/puppet/network/handler/node.rb +++ b/lib/puppet/network/handler/node.rb @@ -2,68 +2,22 @@ # 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 - - def initialize(name, options = {}) - @name = 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 - 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 + # Create a singleton node handler + def self.create + unless @handler + @handler = new + end + @handler 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) @@ -103,6 +57,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) @@ -115,6 +89,10 @@ 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, client = nil, clientip = nil) + return nil unless key + if node = cached?(key) + return node + end facts = node_facts(key) node = nil names = node_names(key, facts) @@ -136,11 +114,15 @@ 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? node.fact_merge(facts) end + + cache(node) + return node else return nil @@ -167,6 +149,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. @@ -180,6 +165,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) @@ -191,7 +193,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. diff --git a/lib/puppet/network/handler/resource.rb b/lib/puppet/network/handler/resource.rb index ca492bd81..ac29dce53 100755 --- a/lib/puppet/network/handler/resource.rb +++ b/lib/puppet/network/handler/resource.rb @@ -26,7 +26,7 @@ class Puppet::Network::Handler # Apply a TransBucket as a transaction. def apply(bucket, format = "yaml", client = nil, clientip = nil) - unless @local + unless local? begin case format when "yaml": @@ -43,7 +43,7 @@ class Puppet::Network::Handler # Create a client, but specify the remote machine as the server # because the class requires it, even though it's unused - client = Puppet::Network::Client.client(:Master).new(:Server => client||"localhost") + client = Puppet::Network::Client.client(:Master).new(:Master => client||"localhost") # Set the objects client.objects = component |
