Please review pull request #643: (#12392) Windows eventlog opened by (joshcooper)

Description:

This commit adds an eventlog log destination and logs to it by default on Windows. As part of this, we now have a puppet message resource dll, which the install.rb script will register during installation. It also adds an acceptance test to verify that messages are being written to the eventlog on Windows.

  • Opened: Mon Apr 09 16:21:39 UTC 2012
  • Based on: puppetlabs:2.7.x (8ceaaf002a5562b6bd78541a25762e1e7740f933)
  • Requested merge: joshcooper:ticket/2.7.x/12392-eventlog (8d2ff895175346ca419e586f6352ec7f8cfdcf06)

Diff follows:

diff --git a/acceptance/tests/windows/eventlog.rb b/acceptance/tests/windows/eventlog.rb
new file mode 100644
index 0000000..ab4f2a6
--- /dev/null
+++ b/acceptance/tests/windows/eventlog.rb
@@ -0,0 +1,26 @@
+test_name "Write to Windows eventlog"
+
+confine :to, :platform => 'windows'
+
+def get_cmd(host)
+  if options[:type] =~ /pe/
+    "#{host['puppetbindir']}/ruby"
+  else
+    'ruby'
+  end
+end
+
+agents.each do |agent|
+  # get remote time
+  now = on(agent, "#{get_cmd(agent)} -e \"puts Time.now.utc.strftime('%m/%d/%Y %H:%M:%S')\"").stdout.chomp
+
+  # generate an error, no master on windows boxes
+  on agent, puppet_agent('--server', '127.0.0.1', '--test'), :acceptable_exit_codes => [1]
+
+  # make sure there's a Puppet error message in the log
+  # cygwin + ssh + wmic hangs trying to read stdin, so echo '' |
+  on agent, "cmd /c echo '' | wmic ntevent where \"LogFile='Application' and SourceName='Puppet' and TimeWritten >= '#{now}'\"  get Message,Type /format:csv" do
+    fail_test "Event not found in Application event log" unless
+      stdout =~ /Could not retrieve catalog from remote server.*,Error/m
+  end
+end
diff --git a/conf/windows/eventlog/Rakefile b/conf/windows/eventlog/Rakefile
new file mode 100755
index 0000000..ff3df33
--- /dev/null
+++ b/conf/windows/eventlog/Rakefile
@@ -0,0 +1,32 @@
+require 'rubygems'
+require 'rake'
+require 'fileutils'
+require 'rbconfig'
+
+BASENAME = "puppetres"
+
+task :default do
+  sh 'rake -T'
+end
+
+desc 'Build puppet eventlog message dll'
+task :dist => ['out', "#{BASENAME}.dll"]
+
+directory 'out'
+
+rule '.rc' => '.mc' do |t|
+  sh "mc -b -r out -h out #{t.source}"
+end
+
+rule '.res' => '.rc' do |t|
+  sh "rc -nologo -r -fo out/#{t.name} out/#{t.source}"
+end
+
+rule '.dll' => '.res' do |t|
+  sh "link -nologo -dll -noentry -machine:x86 -out:out/#{t.name} out/#{t.source}"
+end
+
+desc 'Delete generated files'
+task :clean do
+  FileUtils.rm_rf('out')
+end
diff --git a/conf/windows/eventlog/puppetres.dll b/conf/windows/eventlog/puppetres.dll
new file mode 100755
index 0000000..f894500
Binary files /dev/null and b/conf/windows/eventlog/puppetres.dll differ
diff --git a/conf/windows/eventlog/puppetres.mc b/conf/windows/eventlog/puppetres.mc
new file mode 100755
index 0000000..7a9a05c
--- /dev/null
+++ b/conf/windows/eventlog/puppetres.mc
@@ -0,0 +1,18 @@
+MessageId=0x1
+SymbolicName=PUPPET_INFO
+Language=English
+%1
+.
+
+MessageId=0x2
+SymbolicName=PUPPET_WARN
+Language=English
+%1
+.
+
+MessageId=0x3
+SymbolicName=PUPPET_ERROR
+Language=English
+%1
+.
+
diff --git a/install.rb b/install.rb
index 126461a..a55d929 100755
--- a/install.rb
+++ b/install.rb
@@ -86,7 +86,29 @@ def do_configs(configs, target, strip = 'conf/')
     else
       FileUtils.install(cf, ocf, {:mode => 0644, :verbose => true})
     end
-   end
+  end
+
+  if $operatingsystem == 'windows'
+    src_dll = 'conf/windows/eventlog/puppetres.dll'
+    dst_dll = File.join(InstallOptions.bin_dir, 'puppetres.dll')
+    if $haveftools
+      File.install(src_dll, dst_dll, 0644, true)
+    else
+      FileUtils.install(src_dll, dst_dll, {:mode => 0644, :verbose => true})
+    end
+
+    require 'win32/registry'
+    include Win32::Registry::Constants
+
+    begin
+      Win32::Registry::HKEY_LOCAL_MACHINE.create('SYSTEM\CurrentControlSet\services\eventlog\Application\Puppet', KEY_ALL_ACCESS | 0x0100) do |reg|
+        reg.write_s('EventMessageFile', dst_dll.tr('/', '\\'))
+        reg.write_i('TypesSupported', 0x7)
+      end
+    rescue Win32::Registry::Error => e
+      warn "Failed to create puppet eventlog registry key: #{e}"
+    end
+  end
 end
 
 def do_bins(bins, target, strip = 's?bin/')
diff --git a/lib/puppet/feature/eventlog.rb b/lib/puppet/feature/eventlog.rb
new file mode 100644
index 0000000..e1b909d
--- /dev/null
+++ b/lib/puppet/feature/eventlog.rb
@@ -0,0 +1,6 @@
+require 'puppet/util/feature'
+
+if Puppet.features.microsoft_windows?
+  Puppet.features.rubygems?
+  Puppet.features.add(:eventlog, :libs => %{win32/eventlog})
+end
diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb
index c0a94f9..000e01e 100644
--- a/lib/puppet/util/log.rb
+++ b/lib/puppet/util/log.rb
@@ -196,7 +196,9 @@ def Log.reopen
   end
 
   def self.setup_default
-    Log.newdestination(Puppet.features.syslog? ? :syslog : Puppet[:puppetdlog])
+    Log.newdestination(
+      (Puppet.features.syslog?   ? :syslog   :
+      (Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog])))
   end
 
   # Is the passed level a valid log level?
diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb
index 607e112..364d5ed 100644
--- a/lib/puppet/util/log/destinations.rb
+++ b/lib/puppet/util/log/destinations.rb
@@ -228,3 +228,41 @@ def handle(msg)
   end
 end
 
+Puppet::Util::Log.newdesttype :eventlog do
+  def self.suitable?(obj)
+    Puppet.features.eventlog?
+  end
+
+  def initialize
+    @eventlog = Win32::EventLog.open("Application")
+  end
+
+  def to_native(level)
+    case level
+    when :debug,:info,:notice
+      [Win32::EventLog::INFO, 0x01]
+    when :warning
+      [Win32::EventLog::WARN, 0x02]
+    when :err,:alert,:emerg,:crit
+      [Win32::EventLog::ERROR, 0x03]
+    end
+  end
+
+  def handle(msg)
+    native_type, native_id = to_native(msg.level)
+
+    @eventlog.report_event(
+      :source      => "Puppet",
+      :event_type  => native_type,
+      :event_id    => native_id,
+      :data        ="" (msg.source and msg.source != 'Puppet' ? "#{msg.source}: " : '') + msg.to_s
+    )
+  end
+
+  def close
+    if @eventlog
+      @eventlog.close
+      @eventlog = nil
+    end
+  end
+end
diff --git a/spec/unit/util/log_spec.rb b/spec/unit/util/log_spec.rb
index 6dd365e..18d9b99 100755
--- a/spec/unit/util/log_spec.rb
+++ b/spec/unit/util/log_spec.rb
@@ -22,8 +22,17 @@
       Puppet::Util::Log.setup_default
     end
 
+    it "should fall back to :eventlog" do
+      Puppet.features.stubs(:syslog?).returns(false)
+      Puppet.features.stubs(:eventlog?).returns(true)
+      Puppet::Util::Log.expects(:newdestination).with(:eventlog)
+
+      Puppet::Util::Log.setup_default
+    end
+
     it "should fall back to :file" do
       Puppet.features.stubs(:syslog?).returns(false)
+      Puppet.features.stubs(:eventlog?).returns(false)
       Puppet::Util::Log.expects(:newdestination).with(Puppet[:puppetdlog])
 
       Puppet::Util::Log.setup_default
@@ -72,6 +81,46 @@
     end
   end
 
+  describe Puppet::Util::Log::DestEventlog, :if => Puppet.features.microsoft_windows? do
+    require 'win32/eventlog'
+
+    before :each do
+      Win32::EventLog.stubs(:open).returns(mock 'mylog')
+      Win32::EventLog.stubs(:report_event)
+      Win32::EventLog.stubs(:close)
+      Puppet.features.stubs(:eventlog?).returns(true)
+    end
+
+    it "should restrict its suitability" do
+      Puppet.features.expects(:eventlog?).returns(false)
+
+      Puppet::Util::Log::DestEventlog.suitable?('whatever').should == false
+    end
+
+    it "should open the 'Application' event log" do
+      Win32::EventLog.expects(:open).with('Application')
+
+      Puppet::Util::Log.newdestination(:eventlog)
+    end
+
+    it "should close the event log" do
+      log = mock('myeventlog')
+      log.expects(:close)
+      Win32::EventLog.expects(:open).returns(log)
+
+      Puppet::Util::Log.newdestination(:eventlog)
+      Puppet::Util::Log.close(:eventlog)
+    end
+
+    it "should handle each puppet log level" do
+      log = Puppet::Util::Log::DestEventlog.new
+
+      Puppet::Util::Log.eachlevel do |level|
+        log.to_native(level).should be_is_a(Array)
+      end
+    end
+  end
+
   describe "instances" do
     before do
       Puppet::Util::Log.stubs(:newmessage)

    

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to