require 'openssl'
require 'puppet'
require 'puppet/parser/interpreter'
require 'puppet/sslcertificates'
require 'xmlrpc/server'
require 'yaml'
class Puppet::Network::Handler
class MasterError < Puppet::Error; end
class Master < Handler
desc "Puppet's configuration interface. Used for all interactions related to
generating client configurations."
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" => "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
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 = retobjects.to_yaml(:UseBlock => true)
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: master.rb 2616 2007-06-18 21:03:18Z luke $
syntax highlighted by Code2HTML, v. 0.9.1