summaryrefslogtreecommitdiffstats
path: root/lib/puppet/indirector/code
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2007-09-23 19:04:31 -0500
committerLuke Kanies <luke@madstop.com>2007-09-23 19:04:31 -0500
commitcdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4 (patch)
tree6dab031628ee4c8269c93cd96ed646bdd2874cc4 /lib/puppet/indirector/code
parentc40da335123ee839294b37134d1e6361000bf216 (diff)
downloadpuppet-cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4.tar.gz
puppet-cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4.tar.xz
puppet-cdc8ea6e81c1b5eba5ea784bb7079c4c1f3965a4.zip
Taking a first stab at moving configuration compiling
into the indirection system. There are still quite a few unanswered questions, the two most notable being embodied in unimplemented tests in the Configuration Code terminus. This also requires changing the behaviour in a few places. In particular, 'puppet' and the 'module_puppet' cfengine module need to store a Node object in memory with the appropriate classes, since that's now the only way to communicate with the compiler. That integration work has not yet been done, partially because the old configuration handler (which the soon-to-be-deprecated master handler now uses) still exists.
Diffstat (limited to 'lib/puppet/indirector/code')
-rw-r--r--lib/puppet/indirector/code/configuration.rb171
1 files changed, 171 insertions, 0 deletions
diff --git a/lib/puppet/indirector/code/configuration.rb b/lib/puppet/indirector/code/configuration.rb
new file mode 100644
index 000000000..6d0317204
--- /dev/null
+++ b/lib/puppet/indirector/code/configuration.rb
@@ -0,0 +1,171 @@
+require 'puppet/node'
+require 'puppet/node/configuration'
+require 'puppet/indirector/code'
+require 'puppet/parser/interpreter'
+require 'yaml'
+
+class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code
+ desc "Puppet's configuration compilation interface. Passed a node name
+ or other key, retrieves information about the node (using the ``node_source``)
+ and returns a compiled configuration."
+
+ include Puppet::Util
+
+ attr_accessor :code
+
+ # Compile a node's configuration.
+ def find(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
+
+ # 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 = Puppet::Node.search(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 configuration
+ end
+
+ def initialize
+ set_server_facts
+ end
+
+ # Create/return our interpreter.
+ def interpreter
+ unless defined?(@interpreter) and @interpreter
+ @interpreter = create_interpreter
+ end
+ @interpreter
+ end
+
+ # Return the configuration version.
+ def version(client = nil, clientip = nil)
+ if client and node = Puppet::Node.search(client)
+ update_node_check(node)
+ return interpreter.configuration_version(node)
+ else
+ # Just return something that will always result in a recompile, because
+ # this is local.
+ return (Time.now + 1000).to_i
+ end
+ end
+
+ private
+
+ # 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.merge(@server_facts)
+ end
+
+ # Compile the actual configuration.
+ def compile(node)
+ # 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
+
+ # LAK:FIXME This should log at :none when our client is
+ # local, since we don't want 'puppet' (vs. puppetmasterd) to
+ # log compile times.
+ benchmark(:notice, "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
+
+ return config
+ end
+
+ # Create our interpreter object.
+ def create_interpreter
+ args = {}
+
+ # Allow specification of a code snippet or of a file
+ if self.code
+ args[:Code] = self.code
+ end
+
+ # LAK:FIXME This needs to be handled somehow.
+ #if options.include?(:UseNodes)
+ # args[:UseNodes] = options[:UseNodes]
+ #elsif @local
+ # args[:UseNodes] = false
+ #end
+
+ return Puppet::Parser::Interpreter.new(args)
+ end
+
+ # 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 = {}
+
+ # Add our server version to the fact list
+ @server_facts["serverversion"] = Puppet.version.to_s
+
+ # 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 @server_facts["servername"].nil?
+ host = Facter.value(:hostname)
+ if domain = Facter.value(:domain)
+ @server_facts["servername"] = [host, domain].join(".")
+ else
+ @server_facts["servername"] = host
+ end
+ end
+ end
+
+ # Translate our configuration appropriately for sending back to a client.
+ # LAK:FIXME This method should probably be part of the protocol, but it
+ # shouldn't be here.
+ def translate(config)
+ if local?
+ config
+ else
+ CGI.escape(config.to_yaml(:UseBlock => true))
+ end
+ end
+
+ # Mark that the node has checked in. LAK: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