summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/client.rb
blob: c56b2139360d2a02be630cf0e190ecd8a4551224 (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.method_defined? "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