summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/metric.rb82
-rw-r--r--lib/puppet/reports/rrdgraph.rb113
-rw-r--r--lib/puppet/reports/store.rb7
-rw-r--r--lib/puppet/reports/tagmail.rb24
-rwxr-xr-xtest/other/report.rb18
5 files changed, 195 insertions, 49 deletions
diff --git a/lib/puppet/metric.rb b/lib/puppet/metric.rb
index 6702018dc..3749d257f 100644
--- a/lib/puppet/metric.rb
+++ b/lib/puppet/metric.rb
@@ -32,18 +32,22 @@ module Puppet
end
end
- def create
+ def create(start = nil)
Puppet.config.use(:metrics)
+ start ||= Time.now.to_i - 5
+
path = self.path
args = [
path,
- "--start", Time.now.to_i - 5,
+ "--start", start,
"--step", Puppet[:rrdinterval]
]
- @values.each { |value|
- args.push "DS:%s:GAUGE:600:U:U" % value[0]
+ values.each { |value|
+ # the 7200 is the heartbeat -- this means that any data that isn't
+ # more frequently than every two hours gets thrown away
+ args.push "DS:%s:GAUGE:7200:U:U" % [value[0]]
}
args.push "RRA:AVERAGE:0.5:1:300"
@@ -54,37 +58,50 @@ module Puppet
end
end
+ def dump
+ puts RRD.info(self.path)
+ end
+
def graph(range = nil)
unless Puppet.features.rrd?
Puppet.warning "RRD library is missing; cannot graph metrics"
return
end
- args = [self.path.sub(/rrd$/,"png")]
- args.push("--title",self.label)
- args.push("--imgformat","PNG")
- args.push("--interlace")
+ unit = 60 * 60 * 24
colorstack = %w{#ff0000 #00ff00 #0000ff #099000 #000990 #f00990 #0f0f0f}
- i = 0
- defs = []
- lines = []
- @values.zip(colorstack).each { |value,color|
- next if value.nil?
- # this actually uses the data label
- defs.push("DEF:%s=%s:%s:AVERAGE" % [value[0],self.path,value[0]])
- lines.push("LINE3:%s%s:%s" % [value[0],color,value[1]])
- }
- args << defs
- args << lines
- args.flatten!
- if range
- args.push("--start",range[0],"--end",range[1])
- end
- begin
- RRD.graph(*args)
- rescue => detail
- Puppet.err "Failed to graph %s: %s" % [self.name,detail]
+ {:daily => unit, :weekly => unit * 7, :monthly => unit * 30, :yearly => unit * 365}.each do |name, time|
+ file = self.path.sub(/\.rrd$/, "-%s.png" % name)
+ args = [file]
+
+ args.push("--title",self.label)
+ args.push("--imgformat","PNG")
+ args.push("--interlace")
+ i = 0
+ defs = []
+ lines = []
+ #p @values.collect { |s,l| s }
+ values.zip(colorstack).each { |value,color|
+ next if value.nil?
+ # this actually uses the data label
+ defs.push("DEF:%s=%s:%s:AVERAGE" % [value[0],self.path,value[0]])
+ lines.push("LINE2:%s%s:%s" % [value[0],color,value[1]])
+ }
+ args << defs
+ args << lines
+ args.flatten!
+ if range
+ args.push("--start",range[0],"--end",range[1])
+ else
+ args.push("--start", Time.now.to_i - time, "--end", Time.now.to_i)
+ end
+
+ begin
+ RRD.graph(*args)
+ rescue => detail
+ Puppet.err "Failed to graph %s: %s" % [self.name,detail]
+ end
end
end
@@ -117,21 +134,26 @@ module Puppet
return
end
unless FileTest.exists?(self.path)
- self.create
+ self.create(time - 5)
end
# XXX this is not terribly error-resistant
args = [time]
- @values.each { |value|
+ values.each { |value|
args.push value[2]
}
arg = args.join(":")
begin
- RRD.update(self.path,args.join(":"))
+ RRD.update(self.path,arg)
+ #system("rrdtool updatev %s '%s'" % [self.path, arg])
rescue => detail
raise Puppet::Error, "Failed to update %s: %s" % [self.name,detail]
end
end
+
+ def values
+ @values.sort { |a, b| a[1] <=> b[1] }
+ end
end
end
diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb
index 2c405c051..86f54bf5d 100644
--- a/lib/puppet/reports/rrdgraph.rb
+++ b/lib/puppet/reports/rrdgraph.rb
@@ -1,14 +1,98 @@
require 'puppet'
Puppet::Server::Report.newreport(:rrdgraph) do
- desc "Graph some data about hosts."
+ desc "Graph all available data about hosts using the RRD library. You
+ must have the RRD binary library installed to use this report, which
+ you can get from [Tobias Oetiker's site](http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/pub/contrib/).
+
+ This report will create, manage, and graph RRD database files for each
+ of the metrics generated during transactions, and it will create a
+ few simple html files to display the reporting host's graphs. At this
+ point, it will not create a common index file to display links to
+ all hosts.
+
+ All RRD files and graphs get created in the ``rrddir`` directory. If
+ you want to serve these publicly, you should be able to just alias that
+ directory in a web server."
- def process
- time = Time.now.to_i
+ def hostdir
+ unless defined? @hostdir
+ @hostdir = File.join(Puppet[:rrddir], self.host)
+ end
+ @hostdir
+ end
+
+ def htmlfile(type, graphs, field)
+ file = File.join(hostdir, "%s.html" % type)
+ File.open(file, "w") do |of|
+ of.puts "<html><head><title>%s graphs for %s</title></head><body>" %
+ [type.capitalize, host]
+
+ graphs.each do |graph|
+ if field == :first
+ name = graph.sub(/-\w+.png/, '').capitalize
+ else
+ name = graph.sub(/\w+-/, '').sub(".png", '').capitalize
+ end
+ of.puts "<img src=%s><br>" % graph
+ end
+ of.puts "</body></html>"
+ end
- host = self.host
+ return file
+ end
- hostdir = File.join(Puppet[:rrddir], host)
+ def mkhtml
+ images = Dir.entries(hostdir).find_all { |d| d =~ /\.png/ }
+
+ periodorder = %w{daily weekly monthly yearly}
+
+ periods = {}
+ types = {}
+ images.each do |n|
+ type, period = n.sub(".png", '').split("-")
+ periods[period] ||= []
+ types[type] ||= []
+ periods[period] << n
+ types[type] << n
+ end
+
+ files = []
+ # Make the period html files
+ periodorder.each do |period|
+ unless ary = periods[period]
+ raise "Could not find graphs for %s" % period
+ end
+ files << htmlfile(period, ary, :first)
+ end
+
+ # make the type html files
+ types.sort { |a,b| a[0] <=> b[0] }.each do |type, ary|
+ newary = []
+ periodorder.each do |period|
+ if graph = ary.find { |g| g.include?("-%s.png" % period) }
+ newary << graph
+ else
+ raise "Could not find %s-%s graph" % [type, period]
+ end
+ end
+
+ files << htmlfile(type, newary, :second)
+ end
+
+ File.open(File.join(hostdir, "index.html"), "w") do |of|
+ of.puts "<html><head><title>Report graphs for %s</title></head><body>" %
+ host
+ files.each do |file|
+ of.puts "<a href='#{File.basename(file)}'>%s</a><br/>" %
+ File.basename(file).sub(".html",'').capitalize
+ end
+ of.puts "</body></html>"
+ end
+ end
+
+ def process(time = nil)
+ time ||= Time.now.to_i
unless File.directory?(hostdir)
# Some hackishness to create the dir
@@ -19,19 +103,16 @@ Puppet::Server::Report.newreport(:rrdgraph) do
config.use(:reports)
end
- File.open(File.join(hostdir, "index.html"),"w") { |of|
- of.puts "<html><body>"
- self.metrics.each do |name, metric|
- metric.basedir = hostdir
- metric.store(time)
+ self.metrics.each do |name, metric|
+ metric.basedir = hostdir
+ metric.store(time)
- metric.graph
-
- of.puts "<img src=%s.png><br>" % name
- end
+ metric.graph
+ end
- of.puts "</body></html>"
- }
+ unless FileTest.exists?(File.join(hostdir, "index.html"))
+ mkhtml()
+ end
end
end
diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb
index e4a2b1fe5..e3a3481ad 100644
--- a/lib/puppet/reports/store.rb
+++ b/lib/puppet/reports/store.rb
@@ -12,7 +12,12 @@ Puppet::Server::Report.newreport(:store, :useyaml => true) do
Puppet.config.use(:reporting)
- desc "Store the yaml report on disk"
+ desc "Store the yaml report on disk. Each host sends its report as a YAML dump
+ and this just stores the file on disk, in the ``reportdir`` directory.
+
+ These files collect quickly -- one every half hour -- so it is a good idea
+ to perform some maintenance on them if you use this report (it's the only
+ default report)."
def mkclientdir(client, dir)
config = Puppet::Config.new
diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb
index 2a00ea4c1..6018c6424 100644
--- a/lib/puppet/reports/tagmail.rb
+++ b/lib/puppet/reports/tagmail.rb
@@ -15,7 +15,29 @@ Puppet.config.setdefaults(:reporting,
require 'net/smtp'
Puppet::Server::Report.newreport(:tagmail) do
- desc "Send email reports."
+ desc "This report sends specific log messages to specific email addresses
+ based on the tags in the log messages. See the
+ [tag documentation](../advanced/tags.html) for more information on tags.
+
+ To use this report, you must create a ``tagmail.conf`` (in the location
+ specified by ``tagmap``). This is a simple file that maps tags to
+ email addresses: Any log messages in the report that match the specified
+ tags will be sent to the specified email addresses.
+
+ Tags must be comma-separated, and they can be negated so that messages
+ only match when they do not have that tag. The tags are separated from
+ the email addresses by a colon, and the email addresses should also
+ be comma-separated.
+
+ Lastly, there is an ``all`` tag that will always match all log messages.
+
+ Here is an example tagmail.conf:
+
+ all: me@domain.com
+ webserver, !mailserver: httpadmins@domain.com
+
+ "
+
def process
unless FileTest.exists?(Puppet[:tagmap])
Puppet.notice "Cannot send tagmail report; no tagmap file %s" %
diff --git a/test/other/report.rb b/test/other/report.rb
index 433a0d8d8..939d87aaf 100755
--- a/test/other/report.rb
+++ b/test/other/report.rb
@@ -122,11 +122,27 @@ class TestReports < Test::Unit::TestCase
report.process
}
- hostdir = File.join(Puppet[:rrddir], report.host)
+ hostdir = nil
+ assert_nothing_raised do
+ hostdir = report.hostdir
+ end
+
+ assert(hostdir, "Did not get hostdir back")
assert(FileTest.directory?(hostdir), "Host rrd dir did not get created")
index = File.join(hostdir, "index.html")
assert(FileTest.exists?(index), "index file was not created")
+
+ # Now make sure it creaets each of the rrd files
+ %w{changes resources time}.each do |type|
+ file = File.join(hostdir, "%s.rrd" % type)
+ assert(FileTest.exists?(file), "Did not create rrd file for %s" % type)
+
+ daily = file.sub ".rrd", "-daily.png"
+ assert(FileTest.exists?(daily),
+ "Did not make daily graph for %s" % type)
+ end
+
end
else
$stderr.puts "Install RRD for metric reporting tests"