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 [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.
