Please review pull request #107: Add `defaultroute' fact, improve `ipaddress' fact opened by (syskill)
Description:
Parse netstat output to determine the default route and its associated network
interface. Using this information, we can determine the "real" IP address of a
multi-homed host more accurately.
- Opened: Wed Dec 07 22:42:11 UTC 2011
- Based on: puppetlabs:master (0f9a595c7224a0b5566262647914f52c5c879447)
- Requested merge: syskill:master (7abe5be5d628c3eb249d41df38f6fa8cd8196c0e)
Diff follows:
diff --git a/lib/facter/defaultroute.rb b/lib/facter/defaultroute.rb new file mode 100644 index 0000000..6a7c691 --- /dev/null +++ b/lib/facter/defaultroute.rb @@ -0,0 +1,61 @@ +require 'facter/util/ip' +require 'facter/util/netstat' +require 'ipaddr' + +# Fact: defaultroute +# +# Purpose: Return the default route for a host. +# +# Resolution: +# Runs netstat, and returns the gateway associated with the destination +# "default" or "0.0.0.0". +# +# Caveats: +# + +Facter.add(:defaultroute) do + confine :kernel => Facter::Util::NetStat.supported_platforms + setcode do + Facter::Util::NetStat.get_route_value('default', 'gw') || + Facter::Util::NetStat.get_route_value('0.0.0.0', 'gw') + end +end + +# Fact: defaultroute_interface +# +# Purpose: Return the interface uses for the host's default route. +# +# Resolution: +# Runs netstat, and returns the interface associated with the route for the +# destination "default" or "0.0.0.0". +# +# If the default route listing only includes the gateway and not the +# interface (as is the case on Solaris), return the first interface whose +# network range includes the default gateway. +# +# Caveats: +# + +Facter.add(:defaultroute_interface) do + confine :kernel => Facter::Util::NetStat.supported_platforms + setcode do + Facter::Util::NetStat.get_route_value('default', 'iface') || + Facter::Util::NetStat.get_route_value('0.0.0.0', 'iface') + end +end + +Facter.add(:defaultroute_interface) do + confine :kernel => Facter::Util::IP.supported_platforms + setcode do + return nil unless defaultroute = Facter.value(:defaultroute) + gw = IPAddr.new(defaultroute) + + Facter::Util::IP.get_interfaces.collect { |i| Facter::Util::IP.alphafy(i) }. + detect do |i| + range = Facter.value('network_' + i) + + '/' + + Facter.value('netmask_' + i) + IPAddr.new(range).include?(gw) + end + end +end diff --git a/lib/facter/ipaddress.rb b/lib/facter/ipaddress.rb index 360becd..a50d3ca 100644 --- a/lib/facter/ipaddress.rb +++ b/lib/facter/ipaddress.rb @@ -3,6 +3,9 @@ # Purpose: Return the main IP address for a host. # # Resolution: +# As a first resort, if the interface for the default route could be +# determined (cf. defaultroute.rb), return its IP address. +# # On the Unixes does an ifconfig, and returns the first non 127.0.0.0/8 # subnetted IP it finds. # On Windows, it attempts to use the socket library and resolve the machine's @@ -23,6 +26,17 @@ # Facter.add(:ipaddress) do + setcode do + interface = Facter.value(:defaultroute_interface) + if interface.nil? + nil + else + Facter.value('ipaddress_' + interface) + end + end +end + +Facter.add(:ipaddress) do confine :kernel => :linux setcode do ip = nil diff --git a/lib/facter/util/netstat.rb b/lib/facter/util/netstat.rb new file mode 100644 index 0000000..7a9741a --- /dev/null +++ b/lib/facter/util/netstat.rb @@ -0,0 +1,69 @@ +module Facter::Util::NetStat + COLUMN_MAP = { + :bsd => { + :aliases => [:sunos, :freebsd, :netbsd, :darwin], + :dest => 0, + :gw => 1, + :iface => 5 + }, + :linux => { + :dest => 0, + :gw => 1, + :iface => 7 + }, + :openbsd => { + :dest => 0, + :gw => 1, + :iface => 6 + } + } + + def self.supported_platforms + COLUMN_MAP.inject([]) do |result, tmp| + key, map = tmp + if map[:aliases] + result += map[:aliases] + else + result << key + end + result + end + end + + def self.get_ipv4_output + output = "" + case Facter.value(:kernel) + when 'SunOS', 'FreeBSD', 'NetBSD', 'OpenBSD' + output = %x{/usr/bin/netstat -rn -f inet} + when 'Darwin' + output = %x{/usr/sbin/netstat -rn -f inet} + when 'Linux' + output = %x{/bin/netstat -rn -A inet} + end + output + end + + def self.get_route_value(route, label) + tmp1 = [] + + kernel = Facter.value(:kernel).downcase.to_sym + + # If it's not directly in the map or aliased in the map, then we don't know how to deal with it. + unless map = COLUMN_MAP[kernel] || COLUMN_MAP.values.find { |tmp| tmp[:aliases] and tmp[:aliases].include?(kernel) } + return nil + end + + c1 = map[:dest] + c2 = map[label.to_sym] + + get_ipv4_output.to_a.collect { |s| s.split}.each { |a| + if a[c1] == route + tmp1 << a[c2] + end + } + + if tmp1 + return tmp1.shift + end + end +end
--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to puppet-dev@googlegroups.com.
To unsubscribe from this group, send email to puppet-dev+unsubscr...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.