diff options
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/client.rb | 1 | ||||
-rw-r--r-- | lib/puppet/client/master.rb | 27 | ||||
-rw-r--r-- | lib/puppet/client/reporter.rb | 10 | ||||
-rw-r--r-- | lib/puppet/log.rb | 8 | ||||
-rw-r--r-- | lib/puppet/reports/tagmail.rb | 86 | ||||
-rw-r--r-- | lib/puppet/server.rb | 1 | ||||
-rwxr-xr-x | lib/puppet/server/report.rb | 89 | ||||
-rw-r--r-- | lib/puppet/type.rb | 6 |
8 files changed, 216 insertions, 12 deletions
diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb index b312a78d9..9ee0d55d8 100644 --- a/lib/puppet/client.rb +++ b/lib/puppet/client.rb @@ -169,6 +169,7 @@ module Puppet require 'puppet/client/master' require 'puppet/client/runner' require 'puppet/client/status' + require 'puppet/client/reporter' require 'puppet/client/pelement' end end diff --git a/lib/puppet/client/master.rb b/lib/puppet/client/master.rb index 3f54fa27f..6d57a310a 100644 --- a/lib/puppet/client/master.rb +++ b/lib/puppet/client/master.rb @@ -19,6 +19,15 @@ class Puppet::Client::MasterClient < Puppet::Client ] ) + Puppet.setdefaults(:puppetd, + :reportserver => ["puppet", + "The server to which to send transaction reports." + ], + :report => [false, + "Whether to send reports after every transaction." + ] + ) + Puppet.setdefaults("puppet", :pluginpath => ["$vardir/plugins", "Where Puppet should look for plugins. Multiple directories should @@ -102,6 +111,14 @@ class Puppet::Client::MasterClient < Puppet::Client Metric.graph end + if Puppet[:report] + begin + reportclient().report(transaction) + rescue => detail + Puppet.err "Reporting failed: %s" % detail + end + end + return transaction end @@ -480,6 +497,16 @@ class Puppet::Client::MasterClient < Puppet::Client # Now clean up after ourselves plugins.remove end + + def reportclient + unless defined? @reportclient + @reportclient = Puppet::Client::Reporter.new( + :Server => Puppet[:reportserver] + ) + end + + @reportclient + end end # $Id$ diff --git a/lib/puppet/client/reporter.rb b/lib/puppet/client/reporter.rb index c4f6acd0c..ff80f367d 100644 --- a/lib/puppet/client/reporter.rb +++ b/lib/puppet/client/reporter.rb @@ -13,10 +13,14 @@ class Puppet::Client::Reporter < Puppet::Client::ProxyClient super(hash) end - def report(array) + # Send our report. We get the transaction, and we convert it to a report + # as appropriate. + def report(transaction) # We receive an array of log events, and we need to convert them into # a single big YAML file. + array = transaction.report + report = YAML.dump(array) unless self.local @@ -24,7 +28,9 @@ class Puppet::Client::Reporter < Puppet::Client::ProxyClient end # Now send the report - file = @driver.report(report) + benchmark(:info, "Sent transaction report") do + file = @driver.report(report) + end end end diff --git a/lib/puppet/log.rb b/lib/puppet/log.rb index eb3ea35f6..85058840d 100644 --- a/lib/puppet/log.rb +++ b/lib/puppet/log.rb @@ -478,6 +478,14 @@ module Puppet end end + def tagged?(tag) + @tags.include?(tag) + end + + def to_report + "%s %s (%s): %s" % [self.time, self.source, self.level, self.to_s] + end + def to_s return @message end diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb new file mode 100644 index 000000000..e356b8ba8 --- /dev/null +++ b/lib/puppet/reports/tagmail.rb @@ -0,0 +1,86 @@ +require 'puppet' + +Puppet.config.setdefaults(:reporting, + :tagmap => ["$confdir/tagmap.conf", + "The mapping between reporting tags and email addresses."], + :sendmail => [%x{which sendmail 2>/dev/null}.chomp, + "Where to find the sendmail binary with which to send email."], + :reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."), + "The 'from' email address for the reports."], + :smtpserver => ["none", + "The server through which to send email reports."] +) + +require 'net/smtp' + +Puppet::Server::Report.newreport(:tagmail) do |report| + unless FileTest.exists?(Puppet[:tagmap]) + Puppet.notice "Cannot send tagmail report; not tagmap file %s" % + Puppet[:tagmap] + return + end + + # Load the config file + tags = {} + File.readlines(Puppet[:tagmap]).each do |line| + taglist, emails = line.chomp.split(/\s*:\s*/) + + emails = emails.split(/\s*,\s*/) + taglist.split(/\s*,\s*/).each do |tag| + tags[tag] = emails + end + end + + # Now find any appropriately tagged messages. + reports = {} + tags.each do |tag, emails| + messages = nil + if tag == "all" + messages = report + else + messages = report.find_all do |log| + log.tagged?(tag) + end + end + + if messages and ! messages.empty? + reports[emails] = messages.collect { |m| m.to_report }.join("\n") + end + end + + if Puppet[:smtpserver] != "none" + begin + Net::SMTP.start(Puppet[:smtpserver]) do |smtp| + reports.each do |emails, messages| + Puppet.info "Sending report to %s" % emails.join(", ") + smtp.send_message(messages, Puppet[:reportfrom], *emails) + end + end + rescue => detail + if Puppet[:debug] + puts detail.backtrace + end + raise Puppet::Error, + "Could not send report emails through smtp: %s" % detail + end + elsif Puppet[:sendmail] != "" + begin + reports.each do |emails, messages| + Puppet.info "Sending report to %s" % emails.join(", ") + # We need to open a separate process for every set of email addresses + IO.popen(Puppet[:sendmail] + " " + emails.join(" "), "w") do |p| + p.puts "From: #{Puppet[:reportfrom]}\nSubject: Puppet Report" + p.puts messages + end + end + rescue => detail + if Puppet[:debug] + puts detail.backtrace + end + raise Puppet::Error, + "Could not send report emails via sendmail: %s" % detail + end + else + raise Puppet::Error, "SMTP server is unset and could not find sendmail" + end +end diff --git a/lib/puppet/server.rb b/lib/puppet/server.rb index 62137f8cf..107bf83a2 100644 --- a/lib/puppet/server.rb +++ b/lib/puppet/server.rb @@ -190,6 +190,7 @@ require 'puppet/server/filebucket' require 'puppet/server/pelement' require 'puppet/server/runner' require 'puppet/server/logger' +require 'puppet/server/report' require 'puppet/client' # $Id$ diff --git a/lib/puppet/server/report.rb b/lib/puppet/server/report.rb index 3c1455105..7be44fa44 100755 --- a/lib/puppet/server/report.rb +++ b/lib/puppet/server/report.rb @@ -2,17 +2,63 @@ module Puppet class Server # A simple server for triggering a new run on a Puppet client. class Report < Handler - @interface = XMLRPC::Service::Interface.new("puppetrunner") { |iface| + @interface = XMLRPC::Service::Interface.new("puppetreports") { |iface| iface.add_method("string report(array)") } Puppet.setdefaults(:reporting, - :reportdirectory => ["$vardir/reports", - "The directory in which to store reports received from the - client. Each client gets a separate subdirectory."] + :reportdirectory => {:default => "$vardir/reports", + :mode => 0750, + :owner => "$user", + :group => "$group", + :desc => "The directory in which to store reports received from the + client. Each client gets a separate subdirectory."}, + :reports => ["none", + "The list of reports to generate. All reports are looked for + in puppet/reports/<name>.rb, and multiple report names should be + comma-separated (whitespace is okay)." + ] ) - def initialize + @hooks = {} + + class << self + attr_reader :hooks + end + + # Add a hook for processing reports. + def self.newreport(name, &block) + name = name.intern if name.is_a? String + @hooks[name] = block + end + + def self.report(name) + name = name.intern if name.is_a? String + unless @hooks.include? name + begin + require "puppet/reports/#{name}" + unless @hooks.include? name + Puppet.warning( + "Loaded report file for %s but report was not defined" % + name + ) + return nil + end + rescue LoadError => detail + if Puppet[:debug] + puts detail.backtrace + end + Puppet.warning "Could not load report %s: %s" % + [name, detail] + next + end + end + + @hooks[name] + end + + def initialize(*args) + super Puppet.config.use(:reporting) end @@ -38,6 +84,8 @@ class Server report = CGI.unescape(report) end + process(report) + # We don't want any tracking back in the fs. Unlikely, but there # you go. client.gsub("..",".") @@ -73,6 +121,37 @@ class Server # Our report is in YAML return file end + + private + + # Process the report using all of the existing hooks. + def process(report) + return if Puppet[:reports] == "none" + + # First convert the report to real objects + begin + report = YAML.load(report) + rescue => detail + Puppet.warning "Could not load report: %s" % detail + return + end + + Puppet[:reports].split(/\s*,\s*/).each do |name| + next unless hook = self.class.report(name) + + Puppet.info "Processing report %s" % name + + begin + hook.call(report) + rescue => detail + if Puppet[:debug] + puts detail.backtrace + end + Puppet.err "Report %s failed: %s" % + [name, detail] + end + end + end end end end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 06ebab9e7..752a2ac37 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -242,11 +242,7 @@ class Type < Puppet::Element end rescue LoadError => detail # If we can't load it from there, try loading it as a plugin. - if loadplugin(name) - unless @types.include?(name) - Puppet.warning "Loaded plugin %s but no type was defined" % name - end - end + loadplugin(name) end end |