summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/xmlrpc/client.rb
blob: 790a257bb59a7af9a8c2d95583f1acf0bba3746a (plain)
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
require 'puppet/sslcertificates'
require 'openssl'
require 'puppet/external/base64'

require 'xmlrpc/client'
require 'yaml'

module Puppet::Network
    class ClientError < Puppet::Error; end
    class XMLRPCClientError < Puppet::Error; end
    class XMLRPCClient < ::XMLRPC::Client
        attr_accessor :puppet_server, :puppet_port
        @clients = {}
        @@http_cache = {}

        class << self
            include Puppet::Util
            include Puppet::Util::ClassGen
        end

        # Create a netclient for each handler
        def self.mkclient(handler)
            interface = handler.interface
            namespace = interface.prefix

            # Create a subclass for every client type.  This is
            # so that all of the methods are on their own class,
            # so that they namespaces can define the same methods if
            # they want.
            constant = handler.name.to_s.capitalize
            name = namespace.downcase
            newclient = genclass(name, :hash => @clients,
                :constant => constant)

            interface.methods.each { |ary|
                method = ary[0]
                if public_method_defined?(method)
                    raise Puppet::DevError, "Method %s is already defined" %
                        method
                end
                newclient.send(:define_method,method) { |*args|
                    Puppet.debug "Calling %s.%s" % [namespace, method]
                    begin
                        call("%s.%s" % [namespace, method.to_s],*args)
                    rescue OpenSSL::SSL::SSLError => detail
                        raise XMLRPCClientError,
                            "Certificates were not trusted: %s" % detail
                    rescue ::XMLRPC::FaultException => detail
                        #Puppet.err "Could not call %s.%s: %s" %
                        #    [namespace, method, detail.faultString]
                        #raise XMLRPCClientError,
                        #    "XMLRPC Error: %s" % detail.faultString
                        raise XMLRPCClientError, detail.faultString
                    rescue Errno::ECONNREFUSED => detail
                        msg = "Could not connect to %s on port %s" %
                            [@host, @port]
                        raise XMLRPCClientError, msg
                    rescue SocketError => detail
                        Puppet.err "Could not find server %s: %s" %
                            [@puppet_server, detail.to_s]
                        error = XMLRPCClientError.new(
                            "Could not find server %s" % @puppet_server
                        )
                        error.set_backtrace detail.backtrace
                        raise error
                    rescue => detail
                        Puppet.err "Could not call %s.%s: %s" %
                            [namespace, method, detail.inspect]
                        error = XMLRPCClientError.new(detail.to_s)
                        error.set_backtrace detail.backtrace
                        raise error
                    end
                }
            }

            return newclient
        end

        def self.handler_class(handler)
            @clients[handler] || self.mkclient(handler)
        end

        # Use cert information from a Puppet client to set up the http object.
        def cert_setup(client)
            unless FileTest.exists?(Puppet[:localcacert])
                raise Puppet::SSLCertificates::Support::MissingCertificate,
                    "Could not find ca certificate %s" % Puppet[:localcacert]
            end

            # Pop open @http a little; older versions of Net::HTTP(s) didn't
            # give us a reader for ca_file... Grr...
            class << @http; attr_accessor :ca_file; end

            # Don't want to overwrite certificates, @http will freeze itself
            # once started.
            unless @http.ca_file
                    @http.ca_file = Puppet[:localcacert]
                    store = OpenSSL::X509::Store.new
                    store.add_file Puppet[:localcacert]
                    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
                    @http.cert_store = store
                    @http.cert = client.cert
                    @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
                    @http.key = client.key
            end
        end

        def initialize(hash = {})
            hash[:Path] ||= "/RPC2"
            hash[:Server] ||= Puppet[:server]
            hash[:Port] ||= Puppet[:masterport]
            hash[:HTTPProxyHost] ||= Puppet[:http_proxy_host]
            hash[:HTTPProxyPort] ||= Puppet[:http_proxy_port]

            if "none" == hash[:HTTPProxyHost]
                hash[:HTTPProxyHost] = nil
                hash[:HTTPProxyPort] = nil
            end

            @puppet_server = hash[:Server]
            @puppet_port = hash[:Port]

            super(
                hash[:Server],
                hash[:Path],
                hash[:Port],
                hash[:HTTPProxyHost], # proxy_host
                hash[:HTTPProxyPort], # proxy_port
                nil, # user
                nil, # password
                true, # use_ssl
                120 # a two minute timeout, instead of 30 seconds
            )

            # We overwrite the uninitialized @http here with a cached one.
            key = "%s%s" % [hash[:Server], hash[:Port]]
            if @@http_cache[key]
                @http = @@http_cache[key]
            else
                @@http_cache[key] = @http
            end
        end

        def start
            @http.start unless @http.started?
        end

        def local
            false
        end

        def local?
            false
        end
    end
end