diff options
| author | Jacob Helwig <jacob@puppetlabs.com> | 2011-04-05 15:34:27 -0700 |
|---|---|---|
| committer | Jacob Helwig <jacob@puppetlabs.com> | 2011-04-05 15:34:27 -0700 |
| commit | 356cb8bbd05421346839520f978d4a8ba1b59338 (patch) | |
| tree | 8202a113ebd70efe2f50479d1e542a53c362537d | |
| parent | cc7f0728df96fd36a7ddda9cea3c4db274533f68 (diff) | |
| parent | 5d1cb02cd1ee509647a6dbe157152bd74d5e24ae (diff) | |
Merge branch 'tickets/2.6.x/4339' into 2.6.next
* tickets/2.6.x/4339:
Fix #4339 - Locally save the last report to $lastrunreport
Fix #4339 - Save a last run report summary to $statedir/last_run_summary.yaml
| -rw-r--r-- | lib/puppet/application/agent.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/application/apply.rb | 3 | ||||
| -rw-r--r-- | lib/puppet/configurer.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/defaults.rb | 8 | ||||
| -rw-r--r-- | lib/puppet/indirector/report/yaml.rb | 11 | ||||
| -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/application/agent_spec.rb | 7 | ||||
| -rwxr-xr-x | spec/unit/application/apply_spec.rb | 6 | ||||
| -rwxr-xr-x | spec/unit/configurer_spec.rb | 48 | ||||
| -rw-r--r-- | spec/unit/indirector/report/yaml_spec.rb | 38 | ||||
| -rwxr-xr-x | spec/unit/transaction/report_spec.rb | 15 |
13 files changed, 215 insertions, 23 deletions
diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb index 3749241f8..9f5921de1 100644 --- a/lib/puppet/application/agent.rb +++ b/lib/puppet/application/agent.rb @@ -229,6 +229,8 @@ class Puppet::Application::Agent < Puppet::Application Puppet::SSL::Host.ca_location = options[:fingerprint] ? :none : :remote Puppet::Transaction::Report.terminus_class = :rest + # we want the last report to be persisted locally + Puppet::Transaction::Report.cache_class = :yaml # Override the default; puppetd needs this, usually. # You can still override this on the command-line with, e.g., :compiler. diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index cc733e1f5..7f0b95a89 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -148,6 +148,9 @@ class Puppet::Application::Apply < Puppet::Application exit(1) end + # we want the last report to be persisted locally + Puppet::Transaction::Report.cache_class = :yaml + if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] 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..2a1ded592 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -607,6 +607,14 @@ 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." + }, + :lastrunreport => { :default => "$statedir/last_run_report.yaml", + :mode => 0660, + :desc => "Where puppet agent stores the last run report 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/indirector/report/yaml.rb b/lib/puppet/indirector/report/yaml.rb new file mode 100644 index 000000000..bf7bf4fe5 --- /dev/null +++ b/lib/puppet/indirector/report/yaml.rb @@ -0,0 +1,11 @@ +require 'puppet/transaction/report' +require 'puppet/indirector/yaml' + +class Puppet::Transaction::Report::Yaml < Puppet::Indirector::Yaml + desc "Store last report as a flat file, serialized using YAML." + + # Force report to be saved there + def path(name,ext='.yaml') + Puppet[:lastrunreport] + end +end 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/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index 8f498d4ba..c9d9a4509 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -179,6 +179,7 @@ describe Puppet::Application::Agent do Puppet.settings.stubs(:print_config) Puppet::SSL::Host.stubs(:ca_location=) Puppet::Transaction::Report.stubs(:terminus_class=) + Puppet::Transaction::Report.stubs(:cache_class=) Puppet::Resource::Catalog.stubs(:terminus_class=) Puppet::Resource::Catalog.stubs(:cache_class=) Puppet::Node::Facts.stubs(:terminus_class=) @@ -309,6 +310,12 @@ describe Puppet::Application::Agent do @puppetd.setup end + it "should tell the report handler to cache locally as yaml" do + Puppet::Transaction::Report.expects(:cache_class=).with(:yaml) + + @puppetd.setup + end + it "should change the catalog_terminus setting to 'rest'" do Puppet.expects(:[]=).with(:catalog_terminus, :rest) @puppetd.setup diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index d4f39abe0..67edd4ed7 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -56,6 +56,7 @@ describe Puppet::Application::Apply do Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) + Puppet::Transaction::Report.stubs(:cache_class=) @apply.options.stubs(:[]).with(any_parameters) end @@ -113,6 +114,11 @@ describe Puppet::Application::Apply do lambda { @apply.setup }.should raise_error(SystemExit) end + it "should tell the report handler to cache locally as yaml" do + Puppet::Transaction::Report.expects(:cache_class=).with(:yaml) + + @apply.setup + end end describe "when executing" do 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/indirector/report/yaml_spec.rb b/spec/unit/indirector/report/yaml_spec.rb new file mode 100644 index 000000000..610c9ae43 --- /dev/null +++ b/spec/unit/indirector/report/yaml_spec.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/transaction/report' +require 'puppet/indirector/report/yaml' + +describe Puppet::Transaction::Report::Yaml do + it "should be a subclass of the Yaml terminus" do + Puppet::Transaction::Report::Yaml.superclass.should equal(Puppet::Indirector::Yaml) + end + + it "should have documentation" do + Puppet::Transaction::Report::Yaml.doc.should_not be_nil + end + + it "should be registered with the report indirection" do + indirection = Puppet::Indirector::Indirection.instance(:report) + Puppet::Transaction::Report::Yaml.indirection.should equal(indirection) + end + + it "should have its name set to :yaml" do + Puppet::Transaction::Report::Yaml.name.should == :yaml + end + + it "should inconditionnally save/load from the --lastrunreport setting" do + indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil + Puppet::Indirector::Indirection.stubs(:instance).with(:my_yaml).returns(indirection) + store_class = Class.new(Puppet::Transaction::Report::Yaml) do + def self.to_s + "MyYaml::MyType" + end + end + store = store_class.new + + store.path(:me).should == Puppet[:lastrunreport] + end +end 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 |
