summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/client.rb
blob: f9c50cd60026dd85bbe1e3cd72407a309ebabdaa (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
175
176
177
178
179
# the available clients

require 'puppet'
require 'puppet/network/xmlrpc/client'
require 'puppet/util/subclass_loader'
require 'puppet/util/methodhelper'
require 'puppet/sslcertificates/support'

require 'puppet/network/handler'

require 'net/http'

# Some versions of ruby don't have this method defined, which basically causes
# us to never use ssl.  Yay.
class Net::HTTP
    def use_ssl?
        if defined?(@use_ssl)
            @use_ssl
        else
            false
        end
    end

    # JJM: This is a "backport" of sorts to older ruby versions which
    # do not have this accessor.  See #896 for more information.
    attr_accessor :enable_post_connection_check unless Net::HTTP.instance_methods.include? "enable_post_connection_check"
end

# The base class for all of the clients.  Many clients just directly
# call methods, but some of them need to do some extra work or
# provide a different interface.
class Puppet::Network::Client
    Client = self
    include Puppet::Util
    extend Puppet::Util::SubclassLoader
    include Puppet::Util::MethodHelper

    # This handles reading in the key and such-like.
    include Puppet::SSLCertificates::Support

    attr_accessor :schedule, :lastrun, :local, :stopping

    attr_reader :driver

    # Set up subclass loading
    handle_subclasses :client, "puppet/network/client"

    # Determine what clients look for when being passed an object for local
    # client/server stuff.  E.g., you could call Client::CA.new(:CA => ca).
    def self.drivername
        @drivername ||= self.name
    end

    # Figure out the handler for our client.
    def self.handler
        @handler ||= Puppet::Network::Handler.handler(self.name)
    end

    # The class that handles xmlrpc interaction for us.
    def self.xmlrpc_client
        @xmlrpc_client ||= Puppet::Network::XMLRPCClient.handler_class(self.handler)
    end

    # Create our client.
    def initialize(hash)
        # to whom do we connect?
        @server = nil

        if hash.include?(:Cache)
            @cache = hash[:Cache]
        else
            @cache = true
        end

        driverparam = self.class.drivername
        if hash.include?(:Server)
            args = {:Server => hash[:Server]}
            @server = hash[:Server]
            args[:Port] = hash[:Port] || Puppet[:masterport]

            @driver = self.class.xmlrpc_client.new(args)

            self.read_cert

            # We have to start the HTTP connection manually before we start
            # sending it requests or keep-alive won't work.  Note that with #1010,
            # we don't currently actually want keep-alive.
            @driver.start if @driver.respond_to? :start and Puppet::Network::HttpPool.keep_alive?

            @local = false
        elsif hash.include?(driverparam)
            @driver = hash[driverparam]
            if @driver == true
                @driver = self.class.handler.new
            end
            @local = true
        else
            raise Puppet::Network::ClientError, "#{self.class} must be passed a Server or #{driverparam}"
        end
    end

    # Are we a local client?
    def local?
        if @local
            true
        else
            false
        end
    end

    # Make sure we set the driver up when we read the cert in.
    def recycle_connection
        @driver.recycle_connection if @driver.respond_to?(:recycle_connection)
    end

    # A wrapper method to run and then store the last run time
    def runnow
        if self.stopping
            Puppet.notice "In shutdown progress; skipping run"
            return
        end
        begin
            self.run
            self.lastrun = Time.now.to_i
        rescue => detail
            puts detail.backtrace if Puppet[:trace]
            Puppet.err "Could not run #{self.class}: #{detail}"
        end
    end

    def run
        raise Puppet::DevError, "Client type #{self.class} did not override run"
    end

    def scheduled?
        if sched = self.schedule
            return sched.match?(self.lastrun)
        else
            return true
        end
    end

    def shutdown
        if self.stopping
            Puppet.notice "Already in shutdown"
        else
            self.stopping = true
            Puppet::Util::Storage.store if self.respond_to? :running? and self.running?
            rmpidfile()
        end
    end

    # Start listening for events.  We're pretty much just listening for
    # timer events here.
    def start
        # Create our timer.  Puppet will handle observing it and such.

                    timer = Puppet.newtimer(
                
            :interval => Puppet[:runinterval],
            :tolerance => 1,
        
            :start? => true
        ) do
            begin
                self.runnow if self.scheduled?
            rescue => detail
                puts detail.backtrace if Puppet[:trace]
                Puppet.err "Could not run client; got otherwise uncaught exception: #{detail}"
            end
        end

        # Run once before we start following the timer
        self.runnow
    end

    require 'puppet/network/client/proxy'
end