summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/client.rb1
-rw-r--r--lib/puppet/client/master.rb27
-rw-r--r--lib/puppet/client/reporter.rb10
-rw-r--r--lib/puppet/log.rb8
-rw-r--r--lib/puppet/reports/tagmail.rb86
-rw-r--r--lib/puppet/server.rb1
-rwxr-xr-xlib/puppet/server/report.rb89
-rw-r--r--lib/puppet/type.rb6
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