1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
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 == "")
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$
|