summaryrefslogtreecommitdiffstats
path: root/lib/puppet/networkclient.rb
blob: 09c6bc0abac283032ac5ee9e1681f796bbac271e (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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
require 'puppet'
require 'puppet/sslcertificates'
require 'puppet/type'
require 'facter'
require 'openssl'
require 'puppet/transaction'
require 'puppet/transportable'
require 'puppet/metric'
require 'puppet/daemon'
require 'puppet/server'
require 'puppet/base64'

$noclientnetworking = false
begin
    require 'webrick'
    require 'cgi'
    require 'xmlrpc/client'
    require 'xmlrpc/server'
    require 'yaml'
rescue LoadError => detail
    $noclientnetworking = detail
    raise Puppet::Error, "You must have the Ruby XMLRPC, CGI, and Webrick libraries installed"
end

module Puppet
    class NetworkClientError < RuntimeError; end
    class ClientError < RuntimeError; end
    #---------------------------------------------------------------
    if $noclientnetworking
        Puppet.err "Could not load client network libs: %s" % $noclientnetworking
    else
        class NetworkClient < XMLRPC::Client
            @clients = {}

            # Create a netclient for each handler
            def self.mkclients
                # add the methods associated with each namespace
                Puppet::Server::Handler.each { |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.
                    newclient = Class.new(self)

                    #name = "Puppet::NetworkClient::" + handler.to_s.sub(/^.+::/, '')
                    name = handler.to_s.sub(/^.+::/, '')
                    const_set(name, newclient)
                    @clients[namespace] = newclient

                    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.info "Calling %s" % method
                            #Puppet.info "peer cert is %s" % @http.peer_cert
                            #Puppet.info "cert is %s" % @http.cert
                            begin
                                call("%s.%s" % [namespace, method.to_s],*args)
                            rescue OpenSSL::SSL::SSLError => detail
                                raise NetworkClientError,
                                    "Certificates were not trusted"
                            rescue XMLRPC::FaultException => detail
                                #Puppet.err "Could not call %s.%s: %s" %
                                #    [namespace, method, detail.faultString]
                                #raise NetworkClientError,
                                #    "XMLRPC Error: %s" % detail.faultString
                                raise NetworkClientError, detail.faultString
                            rescue Errno::ECONNREFUSED => detail
                                msg = "Could not connect to %s on port %s" %
                                    [@host, @port]
                                raise NetworkClientError, msg
                            rescue SocketError => detail
                                Puppet.err "Could not find server %s" % @puppetserver
                                exit(12)
                            rescue => detail
                                Puppet.err "Could not call %s.%s: %s" %
                                    [namespace, method, detail.inspect]
                                #raise NetworkClientError.new(detail.to_s)
                                if Puppet[:debug]
                                    puts detail.backtrace
                                end
                                raise
                            end
                        }
                    }
                }
            end

            def self.netclient(namespace)
                if @clients.empty?
                    self.mkclients()
                end

                @clients[namespace]
            end

            def ca_file=(cafile)
                @http.ca_file = cafile
                store = OpenSSL::X509::Store.new
                cacert = OpenSSL::X509::Certificate.new(
                    File.read(cafile)
                )
                store.add_cert(cacert) 
                store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
                @http.cert_store = store
            end

            def cert=(cert)
                #Puppet.debug "Adding certificate"
                @http.cert = cert
                @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
            end

            def key=(key)
                @http.key = key
            end

            def initialize(hash)
                hash[:Path] ||= "/RPC2"
                hash[:Server] ||= "localhost"
                hash[:Port] ||= Puppet[:masterport]

                @puppetserver = hash[:Server]

                super(
                    hash[:Server],
                    hash[:Path],
                    hash[:Port],
                    nil, # proxy_host
                    nil, # proxy_port
                    nil, # user
                    nil, # password
                    true # use_ssl
                )

                if hash[:Certificate]
                    self.cert = hash[:Certificate]
                else
                    unless defined? $nocertwarned
                        Puppet.err "No certificate; running with reduced functionality."
                        $nocertwarned = true
                    end
                end

                if hash[:Key]
                    self.key = hash[:Key]
                end

                if hash[:CAFile]
                    self.ca_file = hash[:CAFile]
                end

                # from here, i need to add the key, cert, and ca cert
                # and reorgize how i start the client
            end

            def local
                false
            end

            def local?
                false
            end
        end
    end
end

# $Id$