[Puppet-dev] [PATCH/facter 1/1] Fixed #3393 - Updates to Facter for MS Windows

14 views
Skip to first unread message

James Turnbull

unread,
Apr 30, 2010, 4:45:59 AM4/30/10
to puppe...@googlegroups.com

Signed-off-by: James Turnbull <ja...@lovedthanlost.net>
---
lib/facter/ipaddress.rb | 32 ++++++-------
lib/facter/kernel.rb | 6 ++-
lib/facter/macaddress.rb | 29 +++++++++---
lib/facter/util/resolution.rb | 100 ++++++++++++++++++++++++++++++++---------
4 files changed, 118 insertions(+), 49 deletions(-)

diff --git a/lib/facter/ipaddress.rb b/lib/facter/ipaddress.rb
index dd0d418..318e10f 100644
--- a/lib/facter/ipaddress.rb
+++ b/lib/facter/ipaddress.rb
@@ -1,3 +1,5 @@
+require 'rbconfig'
+
Facter.add(:ipaddress) do
confine :kernel => :linux
setcode do
@@ -111,30 +113,26 @@ end

Facter.add(:ipaddress) do
confine :kernel => %w{windows}
- setcode do
- ip = nil
- output = %x{ipconfig}
-
- output.split(/^\S/).each { |str|
- if str =~ /IP Address.*: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/
- tmp = $1
- unless tmp =~ /^127\./
- ip = tmp
- break
- end
- end
- }
- ip
- end
+ require 'socket'
+ IPSocket.getaddress(Socket.gethostname)
end

Facter.add(:ipaddress, :ldapname => "iphostnumber", :timeout => 2) do
setcode do
- require 'resolv'
+ if Config::CONFIG['host_os'] =~ /mswin|win32|dos|cygwin|mingw/i
+ require 'win32/resolv'
+ else
+ require 'resolv'
+ end

begin
if hostname = Facter.value(:hostname)
- ip = Resolv.getaddress(hostname)
+ if Config::CONFIG['host_os'] =~ /mswin|win32|dos|cygwin|mingw/i
+ ip = Win32::Resolv.get_resolv_info.last[0]
+ else
+ ip = Resolv.getaddress(hostname)
+ end
+
unless ip == "127.0.0.1"
ip
end
diff --git a/lib/facter/kernel.rb b/lib/facter/kernel.rb
index d68aa3f..66f21ce 100644
--- a/lib/facter/kernel.rb
+++ b/lib/facter/kernel.rb
@@ -2,8 +2,10 @@ Facter.add(:kernel) do
setcode do
require 'rbconfig'
case Config::CONFIG['host_os']
- when /mswin/i; 'windows'
- else Facter::Util::Resolution.exec("uname -s")
+ when /mswin|win32|dos|cygwin|mingw/i
+ 'windows'
+ else
+ Facter::Util::Resolution.exec("uname -s")
end
end
end
diff --git a/lib/facter/macaddress.rb b/lib/facter/macaddress.rb
index e8f40dc..889feea 100644
--- a/lib/facter/macaddress.rb
+++ b/lib/facter/macaddress.rb
@@ -67,13 +67,26 @@ end
Facter.add(:macaddress) do
confine :kernel => %w(windows)
setcode do
- ether = []
- output = %x{ipconfig /all}
- output.split(/\r\n/).each do |str|
- if str =~ /.*Physical Address.*: (\w{1,2}-\w{1,2}-\w{1,2}-\w{1,2}-\w{1,2}-\w{1,2})/
- ether.push($1.gsub(/-/, ":"))
- end
- end
- ether[0]
+ require 'win32ole'
+ require 'socket'
+
+ ether = nil
+ host = Socket.gethostname
+ connect_string = "winmgmts://#{host}/root/cimv2"
+
+ wmi = WIN32OLE.connect(connect_string)
+
+ query = %Q{
+ select *
+ from Win32_NetworkAdapterConfiguration
+ where IPEnabled = True
+ }
+
+ wmi.ExecQuery(query).each{ |nic|
+ ether = nic.MacAddress
+ break
+ }
+
+ ether
end
end
diff --git a/lib/facter/util/resolution.rb b/lib/facter/util/resolution.rb
index b9e28e8..774fb5c 100644
--- a/lib/facter/util/resolution.rb
+++ b/lib/facter/util/resolution.rb
@@ -11,43 +11,99 @@ require 'rbconfig'
class Facter::Util::Resolution
attr_accessor :interpreter, :code, :name, :timeout

- def self.have_which
- if ! defined?(@have_which) or @have_which.nil?
- if Config::CONFIG['host_os'] =~ /mswin/
- @have_which = false
+ WINDOWS = Config::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i
+
+ INTERPRETER = WINDOWS ? 'cmd.exe' : '/bin/sh'
+
+ # Looks for the first occurrence of +program+ within +path+.
+ #
+ # On Windows, it looks for executables ending with the suffixes defined
+ # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
+ # that isn't defined, which you may optionally include in the +program+
+ # name itself.
+ #
+ # Returns nil if not found.
+ #
+ # Examples:
+ #
+ # File.which('ruby') # => '/usr/local/bin/ruby'
+ # File.which('foo') # => nil
+ #
+ # # On Windows
+ # File.which('notepad') # => 'C:/Program Files/Windows'
+ # File.which('notepad.exe') # => 'C:/Program Files/Windows'
+ #--
+ # Borrowed from the ptools library.
+ #
+ def self.which(program, path=ENV['PATH'])
+ programs = [program]
+
+ if WINDOWS
+ if ENV['PATHEXT']
+ extensions = ENV['PATHEXT'].split(';').map{ |e| e.downcase }
else
- %x{which which >/dev/null 2>&1}
- @have_which = ($? == 0)
+ extensions = %w[.exe .com .bat]
end
- end
- @have_which
- end

- # Execute a chunk of code.
- def self.exec(code, interpreter = "/bin/sh")
- raise ArgumentError, "non-sh interpreters are not currently supported" unless interpreter == "/bin/sh"
- binary = code.split(/\s+/).shift
-
- if have_which
- path = nil
- if binary !~ /^\//
- path = %x{which #{binary} 2>/dev/null}.chomp
- # we don't have the binary necessary
- return nil if path == "" or path.match(/Command not found\./)
- else
- path = binary
+ # Push all variants of the program + extension onto the list of
+ # programs to search for.
+ if File.extname(program).empty?
+ unless extensions.include?(File.extname(program).downcase)
+ extensions.each{ |ext|
+ programs.push(program + ext)
+ }
+ end
end
+ end
+
+ # Catch the first path found, or nil
+ location = catch(:done){
+ path.split(File::PATH_SEPARATOR).each{ |dir|
+ programs.each{ |prog|
+ f = File.join(dir, prog)
+ if File.executable?(f) && !File.directory?(f)
+ location = File.join(dir, prog)
+ location.tr!('/', File::ALT_SEPARATOR) if File::ALT_SEPARATOR
+ throw(:done, location)
+ end
+ }
+ }
+ nil # Evaluate to nil if not found
+ }
+
+ location
+ end

- return nil unless FileTest.exists?(path)
+ # Execute a program and return the output of that program.
+ #
+ # Returns nil if the program can't be found, or if there is a problem
+ # executing the code.
+ #
+ def self.exec(code, interpreter = INTERPRETER)
+ raise ArgumentError, "invalid interpreter" unless interpreter == INTERPRETER
+ binary = File.join(File.dirname(code), File.basename(code).split.first)
+
+ path = nil
+
+ # Don't look for the binary if an absolute path is provided
+ if binary !~ /^\/|^\w:[\/\\]/i
+ path = self.which(binary)
+ return nil if path.nil?
+ else
+ path = binary
end

+ return nil unless FileTest.exists?(path)
+
out = nil
+
begin
out = %x{#{code}}.chomp
rescue => detail
$stderr.puts detail
return nil
end
+
if out == ""
return nil
else
--
1.6.6.1

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

David Schmitt

unread,
Apr 30, 2010, 9:32:59 AM4/30/10
to puppe...@googlegroups.com
Shouldn't this use Facter.value(:kernel) instead of duplicating the check?

>
> begin
> if hostname = Facter.value(:hostname)
> - ip = Resolv.getaddress(hostname)
> + if Config::CONFIG['host_os'] =~ /mswin|win32|dos|cygwin|mingw/i

See above.
The following piece has a hole that triggers a spec fail on windows.
Shell-builtins are not checked although they would work when executed.
I've investigated a bit and the problem is, that ruby's %x{} under linux
returns "" instead of failing, when the command cannot be executed. Ruby
on Windows does this differently and raises Errno::ENOENT.

I tried to exploit this and produced an alternative version of this
patch at http://github.com/DavidS/facter/commits/ticket/master/3393
which passes all specs.
> + if File.executable?(f)&& !File.directory?(f)
Best Regards, David
--
dasz.at OG Tel: +43 (0)664 2602670 Web: http://dasz.at
Klosterneuburg UID: ATU64260999

FB-Nr.: FN 309285 g FB-Gericht: LG Korneuburg

Paul Nasrat

unread,
May 27, 2010, 5:43:22 AM5/27/10
to puppe...@googlegroups.com
On 30 April 2010 14:32, David Schmitt <da...@dasz.at> wrote:
On 4/30/2010 10:45 AM, James Turnbull wrote:
 
The following piece has a hole that triggers a spec fail on windows. Shell-builtins are not checked although they would work when executed. I've investigated a bit and the problem is, that ruby's %x{} under linux returns "" instead of failing, when the command cannot be executed. Ruby on Windows does this differently and raises Errno::ENOENT.

I tried to exploit this and produced an alternative version of this patch at http://github.com/DavidS/facter/commits/ticket/master/3393 which passes all specs.

I'm just going through the patch queue and this causes failing specs and all exec based facts to fail on OS X. I'm going to investigate further.

Paul 

Reply all
Reply to author
Forward
0 new messages