summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/server/master.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/network/server/master.rb')
-rw-r--r--lib/puppet/network/server/master.rb212
1 files changed, 212 insertions, 0 deletions
diff --git a/lib/puppet/network/server/master.rb b/lib/puppet/network/server/master.rb
new file mode 100644
index 000000000..b7096cd6d
--- /dev/null
+++ b/lib/puppet/network/server/master.rb
@@ -0,0 +1,212 @@
+require 'openssl'
+require 'puppet'
+require 'puppet/parser/interpreter'
+require 'puppet/sslcertificates'
+require 'xmlrpc/server'
+require 'yaml'
+
+class Puppet::Network::Server
+ class MasterError < Puppet::Error; end
+ class Master < Handler
+ include Puppet::Util
+
+ attr_accessor :ast, :local
+ attr_reader :ca
+
+ @interface = XMLRPC::Service::Interface.new("puppetmaster") { |iface|
+ iface.add_method("string getconfig(string)")
+ 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" => "hostname",
+ "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
+ 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]
+ host = Puppet::Rails::Host.find_or_create_by_name(client)
+ host.last_freshcheck = Time.now
+ if clientip and (! host.ip or host.ip == "")
+ host.ip = clientip
+ end
+ host.save
+ end
+ if defined? @interpreter
+ return @interpreter.parsedate
+ else
+ return 0
+ end
+ end
+
+ def initialize(hash = {})
+ args = {}
+
+ # Allow specification of a code snippet or of a file
+ if code = hash[:Code]
+ args[:Code] = code
+ else
+ args[:Manifest] = hash[:Manifest] || Puppet[:manifest]
+ end
+
+ if hash[:Local]
+ @local = hash[:Local]
+ else
+ @local = false
+ end
+
+ args[:Local] = @local
+
+ if hash.include?(:CA) and hash[:CA]
+ @ca = Puppet::SSLCertificates::CA.new()
+ else
+ @ca = nil
+ end
+
+ Puppet.debug("Creating interpreter")
+
+ if hash.include?(:UseNodes)
+ args[:UseNodes] = hash[: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]
+ end
+
+ @interpreter = 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
+ end
+
+ client, clientip = clientname(client, clientip, facts)
+
+ # Add any server-side facts to our server.
+ addfacts(facts)
+
+ retobjects = nil
+
+ # 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
+ end
+ end
+
+ if @local
+ return retobjects
+ else
+ str = nil
+ case format
+ when "marshal":
+ str = Marshal::dump(retobjects)
+ when "yaml":
+ str = YAML.dump(retobjects)
+ else
+ raise XMLRPC::FaultException.new(
+ 1, "Unavailable config format %s" % format
+ )
+ end
+ return CGI.escape(str)
+ end
+ end
+
+ def local?
+ if defined? @local and @local
+ return true
+ else
+ return false
+ end
+ end
+ end
+end
+
+# $Id$