/lib/facter/util/ip/windows.rb
Ruby | 216 lines | 119 code | 24 blank | 73 comment | 7 complexity | 454b383da3a9c878d5903d3cd81945c7 MD5 | raw file
Possible License(s): Apache-2.0
- # encoding: UTF-8
- require 'facter/util/wmi'
- require 'facter/util/ip'
- require 'facter/util/ip/base'
- class Facter::Util::IP::Windows < Facter::Util::IP::Base
- # The WMI query used to return ip information
- #
- # @return [String]
- #
- # @api private
- WMI_IP_INFO_QUERY = 'SELECT Description, ServiceName, IPAddress, IPConnectionMetric, InterfaceIndex, Index, IPSubnet, MACAddress, MTU, SettingID FROM Win32_NetworkAdapterConfiguration WHERE IPConnectionMetric IS NOT NULL AND IPEnabled = TRUE'
- # Mapping fact names to WMI properties of the Win32_NetworkAdapterConfiguration
- #
- # @api private
- WINDOWS_LABEL_WMI_MAP = {
- :ipaddress => 'IPAddress',
- :ipaddress6 => 'IPAddress',
- :macaddress => 'MACAddress',
- :netmask => 'IPSubnet'
- }
- def self.to_s
- 'windows'
- end
- # Windows doesn't display netmask in hex.
- #
- # @return [Boolean] false by default
- #
- # @api private
- def self.convert_netmask_from_hex?
- false
- end
- # Retrieves a list of unique interfaces names.
- #
- # @return [Array<String>]
- #
- # @api private
- def self.interfaces
- interface_names = []
- network_adapter_configurations.map do |nic|
- Facter::Util::WMI.execquery("SELECT * FROM Win32_NetworkAdapter WHERE Index = #{nic.Index} AND NetEnabled = TRUE").each do |nic|
- interface_names << nic.NetConnectionId unless nic.NetConnectionId.nil? or nic.NetConnectionId.empty?
- end
- end
- interface_names.uniq
- end
- # Get the value of an interface and label. For example, you may want to find
- # the MTU for eth0.
- #
- # @param [String] interface the name of the interface returned by the {#interfaces} method.
- # @param [String] label the type of value to return, e.g. ipaddress
- # @return [String] the value, or nil if not defined
- #
- # @api private
- def self.value_for_interface_and_label(interface, label)
- wmi_value = WINDOWS_LABEL_WMI_MAP[label.downcase.to_sym]
- label_value = nil
- Facter::Util::WMI.execquery("SELECT Index FROM Win32_NetworkAdapter WHERE NetConnectionID = '#{interface}'").each do |nic|
- Facter::Util::WMI.execquery("SELECT #{wmi_value} FROM Win32_NetworkAdapterConfiguration WHERE Index = #{nic.Index}").each do |nic_config|
- case label.downcase.to_sym
- when :ipaddress
- nic_config.IPAddress.any? do |addr|
- label_value = addr if valid_ipv4_address?(addr)
- label_value
- end
- when :ipaddress6
- nic_config.IPAddress.any? do |addr|
- label_value = addr if Facter::Util::IP::Windows.valid_ipv6_address?(addr)
- label_value
- end
- when :netmask
- nic_config.IPSubnet.any? do |addr|
- label_value = addr if Facter::Util::IP::Windows.valid_ipv4_address?(addr)
- label_value
- end
- when :macaddress
- label_value = nic_config.MACAddress
- end
- end
- end
- label_value
- end
- # Returns an array of partial Win32_NetworkAdapterConfiguration objects.
- #
- # @return [Array<WIN32OLE>] objects
- #
- # @api private
- def self.network_adapter_configurations
- nics = []
- # WIN32OLE doesn't implement Enumerable
- Facter::Util::WMI.execquery(WMI_IP_INFO_QUERY).each do |nic|
- nics << nic
- end
- nics
- end
- # Gets a list of active IPv4 network adapter configurations sorted by the
- # lowest IP connection metric. If two configurations have the same metric,
- # then the IPv4 specific binding order as specified in the registry will
- # be used.
- #
- # @return [Array<WIN32OLE>]
- #
- # @api private
- def self.get_preferred_ipv4_adapters
- get_preferred_network_adapters(Bindings4.new)
- end
- # Gets a list of active IPv6 network adapter configurations sorted by the
- # lowest IP connection metric. If two configurations have the same metric,
- # then the IPv6 specific binding order as specified in the registry will
- # be used.
- #
- # @return [Array<WIN32OLE>]
- #
- # @api private
- def self.get_preferred_ipv6_adapters
- get_preferred_network_adapters(Bindings6.new)
- end
- # Gets a list of active network adapter configurations sorted by the lowest
- # IP connection metric. If two configurations have the same metric, then
- # the adapter binding order as specified in the registry will be used.
- # Note the order may different for IPv4 vs IPv6 addresses.
- #
- # @see http://support.microsoft.com/kb/894564
- # @return [Array<WIN32OLE>]
- #
- # @api private
- def self.get_preferred_network_adapters(bindings)
- network_adapter_configurations.select do |nic|
- bindings.bindings.include?(nic.SettingID)
- end.sort do |nic_left,nic_right|
- cmp = nic_left.IPConnectionMetric <=> nic_right.IPConnectionMetric
- if cmp == 0
- bindings.bindings[nic_left.SettingID] <=> bindings.bindings[nic_right.SettingID]
- else
- cmp
- end
- end
- end
- class Bindings4
- def initialize
- @key = 'SYSTEM\CurrentControlSet\Services\Tcpip\Linkage'
- end
- def bindings
- require 'facter/util/registry'
- bindings = {}
- Facter::Util::Registry.hklm_read(@key, 'Bind').each_with_index do |entry, index|
- match_data = entry.match(/\\Device\\(\{.*\})/)
- unless match_data.nil?
- bindings[match_data[1]] = index
- end
- end
- bindings
- rescue
- {}
- end
- end
- class Bindings6 < Bindings4
- def initialize
- @key = 'SYSTEM\CurrentControlSet\Services\Tcpip6\Linkage'
- end
- end
- # Determines if the value passed in is a valid ipv4 address.
- #
- # @param [String] ip_address the IPv4 address to validate
- # @return [Boolean]
- #
- # @api private
- def self.valid_ipv4_address?(ip_address)
- String(ip_address).scan(/(?:[0-9]{1,3}\.){3}[0-9]{1,3}/).each do |match|
- # excluding 169.254.x.x in Windows - this is the DHCP APIPA
- # meaning that if the node cannot get an ip address from the dhcp server,
- # it auto-assigns a private ip address
- unless match == "127.0.0.1" or match =~ /^169.254.*/
- return !!match
- end
- end
- false
- end
- # Determines if the value passed in is a valid ipv6 address.
- #
- # @param [String] ip_address the IPv6 address to validate
- # @return [Boolean]
- #
- # @api private
- def self.valid_ipv6_address?(ip_address)
- String(ip_address).scan(/(?>[0-9,a-f,A-F]*\:{1,2})+[0-9,a-f,A-F]{0,4}/).each do |match|
- unless match =~ /fe80.*/ or match == "::1"
- return !!match
- end
- end
- false
- end
- end