diff options
| author | Brice Figureau <brice-puppet@daysofwonder.com> | 2010-11-12 13:39:41 +0100 |
|---|---|---|
| committer | Jacob Helwig <jacob@puppetlabs.com> | 2011-04-05 15:26:57 -0700 |
| commit | 306aa3032a3770c0d668907ae08042be88ec725e (patch) | |
| tree | ad135ce5b2260a11d5bf3be1ce2b2eccb0731e1f | |
| parent | cc7f0728df96fd36a7ddda9cea3c4db274533f68 (diff) | |
| download | puppet-306aa3032a3770c0d668907ae08042be88ec725e.tar.gz puppet-306aa3032a3770c0d668907ae08042be88ec725e.tar.xz puppet-306aa3032a3770c0d668907ae08042be88ec725e.zip | |
Fix #4339 - Save a last run report summary to $statedir/last_run_summary.yaml
Once a configuration run is done, puppetd will save on the node a
yaml summary report roughly akin to:
---
time:
notify: 0.001025
last_run: 1289561427
schedule: 0.00071
config_retrieval: 0.039518
filebucket: 0.000126
resources:
changed: 1
total: 8
out_of_sync: 1
events:
total: 1
success: 1
changes:
total: 1
This is almost an hash version of the current --summarize output, with
the notable exception that the time section includes the last run unix
timestamp.
The whole idea is to be able to monitor locally if a puppetd does its job.
For instance this could be used in a nagios check or to send an SNMP trap.
The last_run information might help detect staleness, and this summary
can also be used for performance monitoring (ie time section).
The resource section can also show the number of failed resources.
Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
| -rw-r--r-- | lib/puppet/configurer.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/defaults.rb | 4 | ||||
| -rw-r--r-- | lib/puppet/transaction/report.rb | 38 | ||||
| -rw-r--r-- | lib/puppet/util/metric.rb | 8 | ||||
| -rwxr-xr-x | spec/integration/configurer_spec.rb | 43 | ||||
| -rwxr-xr-x | spec/unit/configurer_spec.rb | 48 | ||||
| -rwxr-xr-x | spec/unit/transaction/report_spec.rb | 15 |
7 files changed, 144 insertions, 23 deletions
diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index d3c902576..9f68ca499 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -166,19 +166,28 @@ class Puppet::Configurer execute_postrun_command Puppet::Util::Log.close(report) - send_report(report, transaction) end def send_report(report, trans) report.finalize_report if trans puts report.summary if Puppet[:summarize] + save_last_run_summary(report) report.save if Puppet[:report] rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not send report: #{detail}" end + def save_last_run_summary(report) + Puppet::Util::FileLocking.writelock(Puppet[:lastrunfile], 0660) do |file| + file.print YAML.dump(report.raw_summary) + end + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err "Could not save last run local report: #{detail}" + end + private def self.timeout diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 36159bcf7..23b3b6652 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -607,6 +607,10 @@ module Puppet :report => [false, "Whether to send reports after every transaction." ], + :lastrunfile => { :default => "$statedir/last_run_summary.yaml", + :mode => 0660, + :desc => "Where puppet agent stores the last run report summary in yaml format." + }, :graph => [false, "Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick)."], diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 8e04759ad..16fee42ae 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -80,30 +80,49 @@ class Puppet::Transaction::Report host end - # Provide a summary of this report. + # Provide a human readable textual summary of this report. def summary + report = raw_summary + ret = "" + report.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |key| + ret += "#{Puppet::Util::Metric.labelize(key)}:\n" - @metrics.sort { |a,b| a[1].label <=> b[1].label }.each do |name, metric| - ret += "#{metric.label}:\n" - metric.values.sort { |a,b| + report[key].keys.sort { |a,b| # sort by label - if a[0] == :total + if a == :total 1 - elsif b[0] == :total + elsif b == :total -1 else - a[1] <=> b[1] + report[key][a].to_s <=> report[key][b].to_s end - }.each do |name, label, value| + }.each do |label| + value = report[key][label] next if value == 0 value = "%0.2f" % value if value.is_a?(Float) - ret += " %15s %s\n" % [label + ":", value] + ret += " %15s %s\n" % [Puppet::Util::Metric.labelize(label) + ":", value] end end ret end + # Provide a raw hash summary of this report. + def raw_summary + report = {} + + @metrics.each do |name, metric| + key = metric.name.to_s + report[key] = {} + metric.values.each do |name, label, value| + report[key][name.to_s] = value + end + report[key]["total"] = 0 unless key == "time" or report[key].include?("total") + end + (report["time"] ||= {})["last_run"] = Time.now.tv_sec + report + end + # Based on the contents of this report's metrics, compute a single number # that represents the report. The resulting number is a bitmask where # individual bits represent the presence of different metrics. @@ -142,7 +161,6 @@ class Puppet::Transaction::Report metrics["total"] = resource_statuses.length resource_statuses.each do |name, status| - Puppet::Resource::Status::STATES.each do |state| metrics[state.to_s] += 1 if status.send(state) end diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index 09bbb6137..835e1d610 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -122,7 +122,7 @@ class Puppet::Util::Metric def initialize(name,label = nil) @name = name.to_s - @label = label || labelize(name) + @label = label || self.class.labelize(name) @values = [] end @@ -133,7 +133,7 @@ class Puppet::Util::Metric def newvalue(name,value,label = nil) raise ArgumentError.new("metric name #{name.inspect} is not a string") unless name.is_a? String - label ||= labelize(name) + label ||= self.class.labelize(name) @values.push [name,label,value] end @@ -174,10 +174,8 @@ class Puppet::Util::Metric @values.sort { |a, b| a[1] <=> b[1] } end - private - # Convert a name into a label. - def labelize(name) + def self.labelize(name) name.to_s.capitalize.gsub("_", " ") end end diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index 9a8b66fe4..780c9bbd0 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -5,6 +5,8 @@ require File.dirname(__FILE__) + '/../spec_helper' require 'puppet/configurer' describe Puppet::Configurer do + include PuppetSpec::Files + describe "when downloading plugins" do it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do resource = Puppet::Type.type(:notify).new :name => "yay" @@ -17,19 +19,50 @@ describe Puppet::Configurer do end describe "when running" do - it "should send a transaction report with valid data" do - catalog = Puppet::Resource::Catalog.new - catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing")) + before(:each) do + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing")) - configurer = Puppet::Configurer.new + # Make sure we don't try to persist the local state after the transaction ran, + # because it will fail during test (the state file is in an not existing directory) + # and we need the transaction to be successful to be able to produce a summary report + @catalog.host_config = false + + @configurer = Puppet::Configurer.new + end + + it "should send a transaction report with valid data" do + @configurer.stubs(:save_last_run_summary) Puppet::Transaction::Report.indirection.expects(:save).with do |x, report| report.time.class == Time and report.logs.length > 0 end Puppet[:report] = true - configurer.run :catalog => catalog + @configurer.run :catalog => @catalog + end + + it "should save a correct last run summary" do + report = Puppet::Transaction::Report.new("apply") + report.stubs(:save) + + Puppet[:lastrunfile] = tmpfile("lastrunfile") + Puppet[:report] = true + + @configurer.run :catalog => @catalog, :report => report + + summary = nil + File.open(Puppet[:lastrunfile], "r") do |fd| + summary = YAML.load(fd.read) + end + + summary.should be_a(Hash) + %w{time changes events resources}.each do |key| + summary.should be_key(key) + end + summary["time"].should be_key("notify") + summary["time"]["last_run"].should >= Time.now.tv_sec end end end diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb index cf73a3706..a4b627c08 100755 --- a/spec/unit/configurer_spec.rb +++ b/spec/unit/configurer_spec.rb @@ -81,6 +81,7 @@ describe Puppet::Configurer, "when executing a catalog run" do @catalog = Puppet::Resource::Catalog.new @catalog.stubs(:apply) @agent.stubs(:retrieve_catalog).returns @catalog + @agent.stubs(:save_last_run_summary) Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:close) @@ -225,9 +226,12 @@ describe Puppet::Configurer, "when executing a catalog run" do end describe Puppet::Configurer, "when sending a report" do + include PuppetSpec::Files + before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new + Puppet[:lastrunfile] = tmpfile('last_run_file') @report = Puppet::Transaction::Report.new("apply") @trans = stub 'transaction' @@ -268,6 +272,20 @@ describe Puppet::Configurer, "when sending a report" do @configurer.send_report(@report, nil) end + it "should save the last run summary if reporting is enabled" do + Puppet.settings[:report] = true + + @configurer.expects(:save_last_run_summary).with(@report) + @configurer.send_report(@report, nil) + end + + it "should save the last run summary if reporting is disabled" do + Puppet.settings[:report] = false + + @configurer.expects(:save_last_run_summary).with(@report) + @configurer.send_report(@report, nil) + end + it "should log but not fail if saving the report fails" do Puppet.settings[:report] = true @@ -278,6 +296,36 @@ describe Puppet::Configurer, "when sending a report" do end end +describe Puppet::Configurer, "when saving the summary report file" do + before do + Puppet.settings.stubs(:use).returns(true) + @configurer = Puppet::Configurer.new + + @report = stub 'report' + @trans = stub 'transaction' + @lastrunfd = stub 'lastrunfd' + Puppet::Util::FileLocking.stubs(:writelock).yields(@lastrunfd) + end + + it "should write the raw summary to the lastrunfile setting value" do + Puppet::Util::FileLocking.expects(:writelock).with(Puppet[:lastrunfile], 0660) + @configurer.save_last_run_summary(@report) + end + + it "should write the raw summary as yaml" do + @report.expects(:raw_summary).returns("summary") + @lastrunfd.expects(:print).with(YAML.dump("summary")) + @configurer.save_last_run_summary(@report) + end + + it "should log but not fail if saving the last run summary fails" do + Puppet::Util::FileLocking.expects(:writelock).raises "exception" + Puppet.expects(:err) + lambda { @configurer.save_last_run_summary(@report) }.should_not raise_error + end + +end + describe Puppet::Configurer, "when retrieving a catalog" do before do Puppet.settings.stubs(:use).returns(true) diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 766d4f14d..81efa340e 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -273,8 +273,19 @@ describe Puppet::Transaction::Report do @report.finalize_report end - %w{Changes Total Resources}.each do |main| - it "should include information on #{main} in the summary" do + %w{changes time resources events}.each do |main| + it "should include the key #{main} in the raw summary hash" do + @report.raw_summary.should be_key main + end + end + + it "should include the last run time in the raw summary hash" do + Time.stubs(:now).returns(Time.utc(2010,11,10,12,0,24)) + @report.raw_summary["time"]["last_run"].should == 1289390424 + end + + %w{Changes Total Resources Time Events}.each do |main| + it "should include information on #{main} in the textual summary" do @report.summary.should be_include(main) end end |
