diff options
| author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-02-08 01:39:39 +0000 |
|---|---|---|
| committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-02-08 01:39:39 +0000 |
| commit | 7e07e3dc843798bdbc7a03428ca054adaff2fb72 (patch) | |
| tree | 34d0f9f8c2ee11bdc281e6e4d18cad444253fe36 /lib/puppet/network/client | |
| parent | 6d8068eddd0d29ec53f62557eb53f6ebb8e40591 (diff) | |
| download | puppet-7e07e3dc843798bdbc7a03428ca054adaff2fb72.tar.gz puppet-7e07e3dc843798bdbc7a03428ca054adaff2fb72.tar.xz puppet-7e07e3dc843798bdbc7a03428ca054adaff2fb72.zip | |
Moving all of the client and server code into a single network/ directory. In other words, more code structure cleanup.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2179 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/network/client')
| -rw-r--r-- | lib/puppet/network/client/ca.rb | 23 | ||||
| -rw-r--r-- | lib/puppet/network/client/dipper.rb | 76 | ||||
| -rw-r--r-- | lib/puppet/network/client/file.rb | 20 | ||||
| -rw-r--r-- | lib/puppet/network/client/log.rb | 17 | ||||
| -rw-r--r-- | lib/puppet/network/client/master.rb | 654 | ||||
| -rw-r--r-- | lib/puppet/network/client/proxy.rb | 28 | ||||
| -rw-r--r-- | lib/puppet/network/client/reporter.rb | 34 | ||||
| -rw-r--r-- | lib/puppet/network/client/resource.rb | 65 | ||||
| -rw-r--r-- | lib/puppet/network/client/runner.rb | 17 | ||||
| -rw-r--r-- | lib/puppet/network/client/status.rb | 7 |
10 files changed, 941 insertions, 0 deletions
diff --git a/lib/puppet/network/client/ca.rb b/lib/puppet/network/client/ca.rb new file mode 100644 index 000000000..9a99c1145 --- /dev/null +++ b/lib/puppet/network/client/ca.rb @@ -0,0 +1,23 @@ +require 'puppet/network/client/proxy' + +class Puppet::Network::Client::CA < Puppet::Network::Client::ProxyClient + @drivername = :CA + + # set up the appropriate interface methods + @handler = Puppet::Network::Server::CA + self.mkmethods + + def initialize(hash = {}) + if hash.include?(:CA) + if hash[:CA].is_a? Hash + hash[:CA] = Puppet::Network::Server::CA.new(hash[:CA]) + else + hash[:CA] = Puppet::Network::Server::CA.new() + end + end + + super(hash) + end +end + +# $Id$ diff --git a/lib/puppet/network/client/dipper.rb b/lib/puppet/network/client/dipper.rb new file mode 100644 index 000000000..8eaffc1a0 --- /dev/null +++ b/lib/puppet/network/client/dipper.rb @@ -0,0 +1,76 @@ +# The client class for filebuckets. +class Puppet::Network::Client::Dipper < Puppet::Network::Client + @drivername = :Bucket + + @handler = Puppet::Network::Server::FileBucket + + attr_accessor :name + + # Create our bucket client + def initialize(hash = {}) + if hash.include?(:Path) + bucket = Puppet::Network::Server::FileBucket.new( + :Path => hash[:Path] + ) + hash.delete(:Path) + hash[:Bucket] = bucket + end + + super(hash) + end + + # Back up a file to our bucket + def backup(file) + unless FileTest.exists?(file) + raise(BucketError, "File %s does not exist" % file) + end + contents = File.read(file) + unless local? + contents = Base64.encode64(contents) + end + return @driver.addfile(contents,file) + end + + # Restore the file + def restore(file,sum) + restore = true + if FileTest.exists?(file) + cursum = Digest::MD5.hexdigest(File.read(file)) + + # if the checksum has changed... + # this might be extra effort + if cursum == sum + restore = false + end + end + + if restore + if newcontents = @driver.getfile(sum) + unless local? + newcontents = Base64.decode64(newcontents) + end + tmp = "" + newsum = Digest::MD5.hexdigest(newcontents) + changed = nil + unless FileTest.writable?(file) + changed = File.stat(file).mode + File.chmod(changed | 0200, file) + end + File.open(file,File::WRONLY|File::TRUNC) { |of| + of.print(newcontents) + } + if changed + File.chmod(changed, file) + end + else + Puppet.err "Could not find file with checksum %s" % sum + return nil + end + return newsum + else + return nil + end + end +end + +# $Id$ diff --git a/lib/puppet/network/client/file.rb b/lib/puppet/network/client/file.rb new file mode 100644 index 000000000..7596aec1f --- /dev/null +++ b/lib/puppet/network/client/file.rb @@ -0,0 +1,20 @@ +class Puppet::Network::Client::FileClient < Puppet::Network::Client::ProxyClient + @drivername = :FileServer + + # set up the appropriate interface methods + @handler = Puppet::Network::Server::FileServer + + self.mkmethods + + def initialize(hash = {}) + if hash.include?(:FileServer) + unless hash[:FileServer].is_a?(Puppet::Network::Server::FileServer) + raise Puppet::DevError, "Must pass an actual FS object" + end + end + + super(hash) + end +end + +# $Id$ diff --git a/lib/puppet/network/client/log.rb b/lib/puppet/network/client/log.rb new file mode 100644 index 000000000..eddb8e0ca --- /dev/null +++ b/lib/puppet/network/client/log.rb @@ -0,0 +1,17 @@ +class Puppet::Network::Client::LogClient < Puppet::Network::Client::ProxyClient + @drivername = :Logger + + # set up the appropriate interface methods + @handler = Puppet::Network::Server::Logger + self.mkmethods + + def initialize(hash = {}) + if hash.include?(:Logger) + hash[:Logger] = Puppet::Network::Server::Logger.new() + end + + super(hash) + end +end + +# $Id$ diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb new file mode 100644 index 000000000..9f07f48ef --- /dev/null +++ b/lib/puppet/network/client/master.rb @@ -0,0 +1,654 @@ +# The client for interacting with the puppetmaster config server. +require 'sync' +require 'timeout' + +class Puppet::Network::Client::MasterClient < Puppet::Network::Client + unless defined? @@sync + @@sync = Sync.new + end + + @handler = Puppet::Network::Server::Master + + Puppet.setdefaults("puppetd", + :puppetdlockfile => [ "$statedir/puppetdlock", + "A lock file to temporarily stop puppetd from doing anything."], + :usecacheonfailure => [true, + "Whether to use the cached configuration when the remote + configuration will not compile. This option is useful for testing + new configurations, where you want to fix the broken configuration + rather than reverting to a known-good one." + ], + :downcasefacts => [false, + "Whether facts should be made all lowercase when sent to the server."] + ) + + Puppet.setdefaults(:puppetd, + :configtimeout => [30, + "How long the client should wait for the configuration to be retrieved + before considering it a failure. This can help reduce flapping if too + many clients contact the server at one time." + ], + :reportserver => ["$server", + "The server to which to send transaction reports." + ], + :report => [false, + "Whether to send reports after every transaction." + ] + ) + + # Plugin information. + Puppet.setdefaults("puppet", + :pluginpath => ["$vardir/plugins", + "Where Puppet should look for plugins. Multiple directories should + be colon-separated, like normal PATH variables."], + :plugindest => ["$vardir/plugins", + "Where Puppet should store plugins that it pulls down from the central + server."], + :pluginsource => ["puppet://$server/plugins", + "From where to retrieve plugins. The standard Puppet ``file`` type + is used for retrieval, so anything that is a valid file source can + be used here."], + :pluginsync => [false, + "Whether plugins should be synced with the central server."], + :pluginsignore => [".svn CVS", + "What files to ignore when pulling down plugins."] + ) + + # Central fact information. + Puppet.setdefaults("puppet", + :factpath => ["$vardir/facts", + "Where Puppet should look for facts. Multiple directories should + be colon-separated, like normal PATH variables."], + :factdest => ["$vardir/facts", + "Where Puppet should store facts that it pulls down from the central + server."], + :factsource => ["puppet://$server/facts", + "From where to retrieve facts. The standard Puppet ``file`` type + is used for retrieval, so anything that is a valid file source can + be used here."], + :factsync => [false, + "Whether facts should be synced with the central server."], + :factsignore => [".svn CVS", + "What files to ignore when pulling down facts."] + ) + + @drivername = :Master + + attr_accessor :objects + attr_reader :compile_time + + class << self + # Puppetd should only have one instance running, and we need a way + # to retrieve it. + attr_accessor :instance + include Puppet::Util + end + + def self.facts + # Retrieve the facts from the central server. + if Puppet[:factsync] + self.getfacts() + end + + down = Puppet[:downcasefacts] + + facts = {} + Facter.each { |name,fact| + if down + facts[name] = fact.to_s.downcase + else + facts[name] = fact.to_s + end + } + + # Add our client version to the list of facts, so people can use it + # in their manifests + facts["clientversion"] = Puppet.version.to_s + + facts + end + + # This method actually applies the configuration. + def apply(tags = nil, ignoreschedules = false) + unless defined? @objects + raise Puppet::Error, "Cannot apply; objects not defined" + end + + transaction = @objects.evaluate + + if tags + transaction.tags = tags + end + + if ignoreschedules + transaction.ignoreschedules = true + end + + transaction.addtimes :config_retrieval => @configtime + + begin + transaction.evaluate + rescue Puppet::Error => detail + Puppet.err "Could not apply complete configuration: %s" % + detail + rescue => detail + Puppet.err "Found a bug: %s" % detail + if Puppet[:trace] + puts detail.backtrace + end + ensure + Puppet::Util::Storage.store + end + + if Puppet[:report] + report(transaction) + end + + return transaction + ensure + if defined? transaction and transaction + transaction.cleanup + end + end + + # Cache the config + def cache(text) + Puppet.info "Caching configuration at %s" % self.cachefile + confdir = File.dirname(Puppet[:localconfig]) + File.open(self.cachefile + ".tmp", "w", 0660) { |f| + f.print text + } + File.rename(self.cachefile + ".tmp", self.cachefile) + end + + def cachefile + unless defined? @cachefile + @cachefile = Puppet[:localconfig] + ".yaml" + end + @cachefile + end + + def clear + #@objects = nil + @objects.remove(true) + Puppet::Type.allclear + end + + # Initialize and load storage + def dostorage + begin + Puppet::Util::Storage.load + @compile_time ||= Puppet::Util::Storage.cache(:configuration)[:compile_time] + rescue => detail + if Puppet[:trace] + puts detail.backtrace + end + Puppet.err "Corrupt state file %s: %s" % [Puppet[:statefile], detail] + begin + File.unlink(Puppet[:statefile]) + retry + rescue => detail + raise Puppet::Error.new("Cannot remove %s: %s" % + [Puppet[:statefile], detail]) + end + end + end + + # Check whether our configuration is up to date + def fresh? + unless self.compile_time + return false + end + + # We're willing to give a 2 second drift + if @driver.freshness - @compile_time.to_i < 1 + return true + else + return false + end + end + + # Let the daemon run again, freely in the filesystem. Frolick, little + # daemon! + def enable + Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile]).unlock(:anonymous => true) + end + + # Stop the daemon from making any configuration runs. + def disable + Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile]).lock(:anonymous => true) + end + + # Retrieve the config from a remote server. If this fails, then + # use the cached copy. + def getconfig + dostorage() + if self.fresh? + Puppet.info "Config is up to date" + unless defined? @objects + begin + @objects = YAML.load(self.retrievecache).to_type + rescue => detail + Puppet.warning "Could not load cached configuration: %s" % detail + end + end + return + end + Puppet.debug("getting config") + + # Retrieve the plugins. + if Puppet[:pluginsync] + getplugins() + end + + facts = self.class.facts + + unless facts.length > 0 + raise Puppet::Network::ClientError.new( + "Could not retrieve any facts" + ) + end + + unless objects = get_actual_config(facts) + @objects = nil + return + end + + unless objects.is_a?(Puppet::TransBucket) + raise NetworkClientError, + "Invalid returned objects of type %s" % objects.class + end + + self.setclasses(objects.classes) + + # Clear all existing objects, so we can recreate our stack. + if defined? @objects + Puppet::Type.allclear + + # Make sure all of the objects are really gone. + @objects.remove(true) + end + @objects = nil + + # Now convert the objects to real Puppet objects + @objects = objects.to_type + + if @objects.nil? + raise Puppet::Error, "Configuration could not be processed" + end + + # and perform any necessary final actions before we evaluate. + @objects.finalize + + return @objects + end + + # A simple proxy method, so it's easy to test. + def getplugins + self.class.getplugins + end + + # Just so we can specify that we are "the" instance. + def initialize(*args) + Puppet.config.use(:puppet, :sslcertificates, :puppetd) + super + + # This might be nil + @configtime = 0 + + self.class.instance = self + @running = false + + mkdefault_objects + end + + # Make the default objects necessary for function. + def mkdefault_objects + # First create the default scheduling objects + Puppet::Type.type(:schedule).mkdefaultschedules + + # And filebuckets + Puppet::Type.type(:filebucket).mkdefaultbucket + end + + # Mark that we should restart. The Puppet module checks whether we're running, + # so this only gets called if we're in the middle of a run. + def restart + # If we're currently running, then just mark for later + Puppet.notice "Received signal to restart; waiting until run is complete" + @restart = true + end + + # Should we restart? + def restart? + if defined? @restart + @restart + else + false + end + end + + # Retrieve the cached config + def retrievecache + if FileTest.exists?(self.cachefile) + return File.read(self.cachefile) + else + return "" + end + end + + # The code that actually runs the configuration. + def run(tags = nil, ignoreschedules = false) + lockfile = Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile]) + + Puppet::Util.sync(:puppetrun).synchronize(Sync::EX) do + if !lockfile.lock + Puppet.notice "Lock file %s exists; skipping configuration run" % + lockfile.lockfile + else + @running = true + @configtime = thinmark do + self.getconfig + end + + if defined? @objects and @objects + unless @local + Puppet.notice "Starting configuration run" + end + benchmark(:notice, "Finished configuration run") do + self.apply(tags, ignoreschedules) + end + end + @running = false + end + + lockfile.unlock + + # Did we get HUPped during the run? If so, then restart now that we're + # done with the run. + if self.restart? + Process.kill(:HUP, $$) + end + end + end + + def running? + @running + end + + # Store the classes in the classfile, but only if we're not local. + def setclasses(ary) + if @local + return + end + unless ary and ary.length > 0 + Puppet.info "No classes to store" + return + end + begin + File.open(Puppet[:classfile], "w") { |f| + f.puts ary.join("\n") + } + rescue => detail + Puppet.err "Could not create class file %s: %s" % + [Puppet[:classfile], detail] + end + end + + private + + # Download files from the remote server, returning a list of all + # changed files. + def self.download(args) + objects = Puppet::Type.type(:component).create( + :name => "#{args[:name]}_collector" + ) + hash = { + :path => args[:dest], + :recurse => true, + :source => args[:source], + :tag => "#{args[:name]}s", + :owner => Process.uid, + :group => Process.gid, + :backup => false + } + + if args[:ignore] + hash[:ignore] = args[:ignore].split(/\s+/) + end + objects.push Puppet::Type.type(:file).create(hash) + + Puppet.info "Retrieving #{args[:name]}s" + + begin + trans = objects.evaluate + trans.ignoretags = true + Timeout::timeout(self.timeout) do + trans.evaluate + end + rescue Puppet::Error, Timeout::Error => detail + if Puppet[:debug] + puts detail.backtrace + end + Puppet.err "Could not retrieve #{args[:name]}s: %s" % detail + end + + # Now source all of the changed objects, but only source those + # that are top-level. + files = [] + trans.changed?.find_all do |object| + yield object if block_given? + files << object[:path] + end + trans.cleanup + + # Now clean up after ourselves + objects.remove + files + end + + # Retrieve facts from the central server. + def self.getfacts + # Clear all existing definitions. + Facter.clear + + # Download the new facts + path = Puppet[:factpath].split(":") + files = [] + download(:dest => Puppet[:factdest], :source => Puppet[:factsource], + :ignore => Puppet[:factsignore], :name => "fact") do |object| + + next unless path.include?(File.dirname(object[:path])) + + files << object[:path] + + end + ensure + # Reload everything. + if Facter.respond_to? :loadfacts + Facter.loadfacts + elsif Facter.respond_to? :load + Facter.load + else + raise Puppet::Error, + "You must upgrade your version of Facter to use centralized facts" + end + + # This loads all existing facts and any new ones. We have to remove and + # reload because there's no way to unload specific facts. + loadfacts() + end + + # Retrieve the plugins from the central server. We only have to load the + # changed plugins, because Puppet::Type loads plugins on demand. + def self.getplugins + path = Puppet[:pluginpath].split(":") + download(:dest => Puppet[:plugindest], :source => Puppet[:pluginsource], + :ignore => Puppet[:pluginsignore], :name => "plugin") do |object| + + next unless path.include?(File.dirname(object[:path])) + + begin + Puppet.info "Reloading plugin %s" % + File.basename(File.basename(object[:path])).sub(".rb",'') + load object[:path] + rescue => detail + Puppet.warning "Could not reload plugin %s: %s" % + [object[:path], detail] + end + end + end + + def self.loaddir(dir, type) + return unless FileTest.directory?(dir) + + Dir.entries(dir).find_all { |e| e =~ /\.rb$/ }.each do |file| + fqfile = File.join(dir, file) + begin + Puppet.info "Loading #{type} %s" % File.basename(file.sub(".rb",'')) + Timeout::timeout(self.timeout) do + load fqfile + end + rescue => detail + Puppet.warning "Could not load #{type} %s: %s" % [fqfile, detail] + end + end + end + + def self.loadfacts + Puppet[:factpath].split(":").each do |dir| + loaddir(dir, "fact") + end + end + + def self.timeout + @timeout = Puppet[:configtimeout] + case @timeout + when String: + if @timeout =~ /^\d+$/ + @timeout = Integer(@timeout) + else + raise ArgumentError, "Configuration timeout must be an integer" + end + when Integer: # nothing + else + raise ArgumentError, "Configuration timeout must be an integer" + end + end + + # Send off the transaction report. + def report(transaction) + begin + report = transaction.report() + if Puppet[:rrdgraph] == true + report.graph() + end + reportclient().report(report) + rescue => detail + Puppet.err "Reporting failed: %s" % detail + end + end + + def reportclient + unless defined? @reportclient + @reportclient = Puppet::Network::Client::Reporter.new( + :Server => Puppet[:reportserver] + ) + end + + @reportclient + end + + loadfacts() + + private + + # Actually retrieve the configuration, either from the server or from a local master. + def get_actual_config(facts) + if @local + return get_local_config(facts) + else + begin + Timeout::timeout(self.class.timeout) do + return get_remote_config(facts) + end + rescue Timeout::Error + Puppet.err "Configuration retrieval timed out" + return nil + end + end + end + + # Retrieve a configuration from a local master. + def get_local_config(facts) + # If we're local, we don't have to do any of the conversion + # stuff. + objects = @driver.getconfig(facts, "yaml") + @compile_time = Time.now + + if objects == "" + raise Puppet::Error, "Could not retrieve configuration" + end + + return objects + end + + # Retrieve a config from a remote master. + def get_remote_config(facts) + textobjects = "" + + textfacts = CGI.escape(YAML.dump(facts)) + + benchmark(:debug, "Retrieved configuration") do + # error handling for this is done in the network client + begin + textobjects = @driver.getconfig(textfacts, "yaml") + rescue => detail + Puppet.err "Could not retrieve configuration: %s" % detail + + unless Puppet[:usecacheonfailure] + @objects = nil + Puppet.warning "Not using cache on failed configuration" + return + end + end + end + + fromcache = false + if textobjects == "" + textobjects = self.retrievecache + if textobjects == "" + raise Puppet::Error.new( + "Cannot connect to server and there is no cached configuration" + ) + end + Puppet.warning "Could not get config; using cached copy" + fromcache = true + else + @compile_time = Time.now + Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time + end + + begin + textobjects = CGI.unescape(textobjects) + rescue => detail + raise Puppet::Error, "Could not CGI.unescape configuration" + end + + if @cache and ! fromcache + self.cache(textobjects) + end + + begin + objects = YAML.load(textobjects) + rescue => detail + raise Puppet::Error, + "Could not understand configuration: %s" % + detail.to_s + end + + return objects + end +end + +# $Id$ diff --git a/lib/puppet/network/client/proxy.rb b/lib/puppet/network/client/proxy.rb new file mode 100644 index 000000000..e1295a96f --- /dev/null +++ b/lib/puppet/network/client/proxy.rb @@ -0,0 +1,28 @@ +# unlike the other client classes (again, this design sucks) this class +# is basically just a proxy class -- it calls its methods on the driver +# and that's about it +class Puppet::Network::Client::ProxyClient < Puppet::Network::Client + def self.mkmethods + interface = @handler.interface + namespace = interface.prefix + + + interface.methods.each { |ary| + method = ary[0] + Puppet.debug "%s: defining %s.%s" % [self, namespace, method] + define_method(method) { |*args| + begin + @driver.send(method, *args) + 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 + end + } + } + end +end + +# $Id$ diff --git a/lib/puppet/network/client/reporter.rb b/lib/puppet/network/client/reporter.rb new file mode 100644 index 000000000..dd340da02 --- /dev/null +++ b/lib/puppet/network/client/reporter.rb @@ -0,0 +1,34 @@ +class Puppet::Network::Client::Reporter < Puppet::Network::Client + @drivername = :Report + + # set up the appropriate interface methods + @handler = Puppet::Network::Server::Report + + def initialize(hash = {}) + if hash.include?(:Report) + hash[:Report] = Puppet::Network::Server::Report.new() + end + + super(hash) + end + + # Send our report. We get the transaction report and convert it to YAML + # as appropriate. + def report(transreport) + report = YAML.dump(transreport) + + unless self.local + report = CGI.escape(report) + end + + # Now send the report + file = nil + benchmark(:info, "Sent transaction report") do + file = @driver.report(report) + end + + file + end +end + +# $Id$ diff --git a/lib/puppet/network/client/resource.rb b/lib/puppet/network/client/resource.rb new file mode 100644 index 000000000..71a19bf91 --- /dev/null +++ b/lib/puppet/network/client/resource.rb @@ -0,0 +1,65 @@ +class Puppet::Network::Client::Resource < Puppet::Network::Client + @drivername = :ResourceServer + + @handler = Puppet::Network::Server::Resource + + def apply(bucket) + + case bucket + when Puppet::TransObject + tmp = Puppet::TransBucket.new + tmp.push bucket + bucket = tmp + bucket.name = Facter["hostname"].value + bucket.type = "resource" + when Puppet::TransBucket + # nothing + else + raise Puppet::DevError, "You must pass a transportable object, not a %s" % + bucket.class + end + + unless @local + bucket = Base64.encode64(YAML::dump(bucket)) + end + report = @driver.apply(bucket, "yaml") + + return report + end + + def describe(type, name, retrieve = false, ignore = false) + Puppet.info "Describing %s[%s]" % [type.to_s.capitalize, name] + text = @driver.describe(type, name, retrieve, ignore, "yaml") + + object = nil + if @local + object = text + else + object = YAML::load(Base64.decode64(text)) + end + + return object + end + + def initialize(hash = {}) + if hash.include?(:ResourceServer) + unless hash[:ResourceServer].is_a?(Puppet::Network::Server::Resource) + raise Puppet::DevError, "Must pass an actual PElement server object" + end + end + + super(hash) + end + + def list(type, ignore = false, base = false) + bucket = @driver.list(type, ignore, base, "yaml") + + unless @local + bucket = YAML::load(Base64.decode64(bucket)) + end + + return bucket + end +end + +# $Id$ diff --git a/lib/puppet/network/client/runner.rb b/lib/puppet/network/client/runner.rb new file mode 100644 index 000000000..40d13ac86 --- /dev/null +++ b/lib/puppet/network/client/runner.rb @@ -0,0 +1,17 @@ +class Puppet::Network::Client::Runner < Puppet::Network::Client::ProxyClient + @drivername = :Runner + + # set up the appropriate interface methods + @handler = Puppet::Network::Server::Runner + self.mkmethods + + def initialize(hash = {}) + if hash.include?(:Runner) + hash[:Runner] = Puppet::Network::Server::Runner.new() + end + + super(hash) + end +end + +# $Id$ diff --git a/lib/puppet/network/client/status.rb b/lib/puppet/network/client/status.rb new file mode 100644 index 000000000..6c1a96e85 --- /dev/null +++ b/lib/puppet/network/client/status.rb @@ -0,0 +1,7 @@ +class Puppet::Network::Client::StatusClient < Puppet::Network::Client::ProxyClient + # set up the appropriate interface methods + @handler = Puppet::Network::Server::ServerStatus + self.mkmethods +end + +# $Id$ |
