1
require 'puppet/sslcertificates'
2
require 'puppet/network/http_pool'
4
require 'puppet/external/base64'
6
require 'xmlrpc/client'
10
module Puppet::Network
11
class ClientError < Puppet::Error; end
12
class XMLRPCClientError < Puppet::Error; end
13
class XMLRPCClient < ::XMLRPC::Client
15
attr_accessor :puppet_server, :puppet_port
20
include Puppet::Util::ClassGen
23
# Create a netclient for each handler
24
def self.mkclient(handler)
25
interface = handler.interface
26
namespace = interface.prefix
28
# Create a subclass for every client type. This is
29
# so that all of the methods are on their own class,
30
# so that their namespaces can define the same methods if
32
constant = handler.name.to_s.capitalize
33
name = namespace.downcase
34
newclient = genclass(name, :hash => @clients, :constant => constant)
36
interface.methods.each { |ary|
38
newclient.send(:define_method,method) { |*args|
39
make_rpc_call(namespace, method, *args)
46
def self.handler_class(handler)
47
@clients[handler] || self.mkclient(handler)
51
def initialize(&block)
52
singleton_class.define_method(:execute, &block)
56
# Use a class variable so all subclasses have access to it.
59
def self.error_handler(exception)
60
if handler = @@error_handlers[exception.class]
63
return @@error_handlers[:default]
67
def self.handle_error(*exceptions, &block)
68
handler = ErrorHandler.new(&block)
70
exceptions.each do |exception|
71
@@error_handlers[exception] = handler
75
handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
76
if detail.message =~ /bad write retry/
77
Puppet.warning "Transient SSL write error; restarting connection and retrying"
78
client.recycle_connection
81
["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
82
Puppet.warning "Certificate validation failed; consider using the certname configuration option" if detail.message.include?(str)
84
raise XMLRPCClientError, "Certificates were not trusted: #{detail}"
87
handle_error(:default) do |client, detail, namespace, method|
88
if detail.message.to_s =~ /^Wrong size\. Was \d+, should be \d+$/
89
Puppet.warning "XMLRPC returned wrong size. Retrying."
92
Puppet.err "Could not call #{namespace}.#{method}: #{detail.inspect}"
93
error = XMLRPCClientError.new(detail.to_s)
94
error.set_backtrace detail.backtrace
98
handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
99
if detail.message =~ /bad write retry/
100
Puppet.warning "Transient SSL write error; restarting connection and retrying"
101
client.recycle_connection
104
["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
105
Puppet.warning "Certificate validation failed; consider using the certname configuration option" if detail.message.include?(str)
107
raise XMLRPCClientError, "Certificates were not trusted: #{detail}"
110
handle_error(::XMLRPC::FaultException) do |client, detail, namespace, method|
111
raise XMLRPCClientError, detail.faultString
114
handle_error(Errno::ECONNREFUSED) do |client, detail, namespace, method|
115
msg = "Could not connect to #{client.host} on port #{client.port}"
116
raise XMLRPCClientError, msg
119
handle_error(SocketError) do |client, detail, namespace, method|
120
Puppet.err "Could not find server #{@host}: #{detail}"
121
error = XMLRPCClientError.new("Could not find server #{client.host}")
122
error.set_backtrace detail.backtrace
126
handle_error(Errno::EPIPE, EOFError) do |client, detail, namespace, method|
127
Puppet.info "Other end went away; restarting connection and retrying"
128
client.recycle_connection
132
handle_error(Timeout::Error) do |client, detail, namespace, method|
133
Puppet.err "Connection timeout calling #{namespace}.#{method}: #{detail}"
134
error = XMLRPCClientError.new("Connection Timeout")
135
error.set_backtrace(detail.backtrace)
139
def make_rpc_call(namespace, method, *args)
140
Puppet.debug "Calling #{namespace}.#{method}"
142
call("#{namespace}.#{method}",*args)
143
rescue SystemExit,NoMemoryError
145
rescue Exception => detail
146
retry if self.class.error_handler(detail).execute(self, detail, namespace, method) == :retry
149
http.finish if http.started?
153
@http ||= Puppet::Network::HttpPool.http_instance(host, port, true)
156
attr_reader :host, :port
158
def initialize(hash = {})
159
hash[:Path] ||= "/RPC2"
160
hash[:Server] ||= Puppet[:server]
161
hash[:Port] ||= Puppet[:masterport]
162
hash[:HTTPProxyHost] ||= Puppet[:http_proxy_host]
163
hash[:HTTPProxyPort] ||= Puppet[:http_proxy_port]
165
if "none" == hash[:HTTPProxyHost]
166
hash[:HTTPProxyHost] = nil
167
hash[:HTTPProxyPort] = nil
176
hash[:HTTPProxyHost],
177
hash[:HTTPProxyPort],
182
Puppet[:configtimeout] # use configured timeout (#1176)
184
@http = Puppet::Network::HttpPool.http_instance(@host, @port)
187
# Get rid of our existing connection, replacing it with a new one.
188
# This should only happen if we lose our connection somehow (e.g., an EPIPE)
189
# or we've just downloaded certs and we need to create new http instances
190
# with the certs added.
191
def recycle_connection
192
http.finish if http.started?
194
self.http # force a new one
198
@http.start unless @http.started?
200
Puppet.err "Could not connect to server: #{detail}"