Merge pull request #6386 from legal90/fix-osx-nic-order

Fix network configuration in OS X (Darwin) guests
This commit is contained in:
Mitchell Hashimoto 2015-11-18 14:33:51 -08:00
commit d69d7047b2
6 changed files with 99 additions and 35 deletions

View File

@ -240,6 +240,10 @@ module Vagrant
error_key(:bundler_error)
end
class CantReadMACAddresses < VagrantError
error_key(:cant_read_mac_addresses)
end
class CapabilityHostExplicitNotDetected < VagrantError
error_key(:capability_host_explicit_not_detected)
end

View File

@ -6,45 +6,109 @@ module VagrantPlugins
module GuestDarwin
module Cap
class ConfigureNetworks
@@logger = Log4r::Logger.new("vagrant::guest::darwin::configure_networks")
include Vagrant::Util
def self.configure_networks(machine, networks)
# Slightly different than other plugins, using the template to build commands
# rather than templating the files.
if !machine.provider.capability?(:nic_mac_addresses)
raise Vagrant::Errors::CantReadMACAddresses,
provider: machine.provider_name.to_s
end
machine.communicate.sudo("networksetup -detectnewhardware")
machine.communicate.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces")
tmpints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
machine.communicate.download("/tmp/vagrant.interfaces",tmpints)
nic_mac_addresses = machine.provider.capability(:nic_mac_addresses)
@@logger.debug("mac addresses: #{nic_mac_addresses.inspect}")
devlist = []
ints = ::IO.read(tmpints)
mac_service_map = create_mac_service_map(machine)
networks.each do |network|
mac_address = nic_mac_addresses[network[:interface]+1]
if mac_address.nil?
@@logger.warn("Could not find mac address for network #{network.inspect}")
next
end
service_name = mac_service_map[mac_address]
if service_name.nil?
@@logger.warn("Could not find network service for mac address #{mac_address}")
next
end
network_type = network[:type].to_sym
if network_type == :static
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
elsif network_type == :dhcp
command = "networksetup -setdhcp \"#{service_name}\""
else
raise "#{network_type} network type is not supported, try static or dhcp"
end
machine.communicate.sudo(command)
end
end
# Creates a hash mapping MAC addresses to network service name
# Example: { "00C100A1B2C3" => "Thunderbolt Ethernet" }
def self.create_mac_service_map(machine)
tmp_ints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
tmp_hw = File.join(Dir.tmpdir, File.basename("#{machine.id}.hardware"))
machine.communicate.tap do |comm|
comm.sudo("networksetup -detectnewhardware")
comm.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces")
comm.sudo("networksetup -listallhardwareports > /tmp/vagrant.hardware")
comm.download("/tmp/vagrant.interfaces", tmp_ints)
comm.download("/tmp/vagrant.hardware", tmp_hw)
end
interface_map = {}
ints = ::IO.read(tmp_ints)
ints.split(/\n\n/m).each do |i|
if i.match(/Hardware/) and not i.match(/Ethernet/).nil?
devmap = {}
if i.match(/Hardware/) && i.match(/Ethernet/)
# Ethernet, should be 2 lines,
# (3) Thunderbolt Ethernet
# (Hardware Port: Thunderbolt Ethernet, Device: en1)
# multiline, should match "Thunderbolt Ethernet", "en1"
devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m)
devmap[:interface] = devicearry[2]
devmap[:service] = devicearry[1]
devlist << devmap
service = devicearry[1]
interface = devicearry[2]
# Should map interface to service { "en1" => "Thunderbolt Ethernet" }
interface_map[interface] = service
end
end
File.delete(tmpints)
File.delete(tmp_ints)
networks.each do |network|
service_name = devlist[network[:interface]][:service]
if network[:type].to_sym == :static
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
elsif network[:type].to_sym == :dhcp
command = "networksetup -setdhcp \"#{service_name}\""
mac_service_map = {}
macs = ::IO.read(tmp_hw)
macs.split(/\n\n/m).each do |i|
if i.match(/Hardware/) && i.match(/Ethernet/)
# Ethernet, should be 3 lines,
# Hardware Port: Thunderbolt 1
# Device: en1
# Ethernet Address: a1:b2:c3:d4:e5:f6
# multiline, should match "en1", "00:c1:00:a1:b2:c3"
devicearry = i.match(/Device: (.+)\nEthernet Address: (.+)/m)
interface = devicearry[1]
naked_mac = devicearry[2].gsub(':','').upcase
# Skip hardware ports without MAC (bridges, bluetooth, etc.)
next if naked_mac == "N/A"
if !interface_map[interface]
@@logger.warn("Could not find network service for interface #{interface}")
next
end
# Should map MAC to service, { "00C100A1B2C3" => "Thunderbolt Ethernet" }
mac_service_map[naked_mac] = interface_map[interface]
end
machine.communicate.sudo(command)
end
File.delete(tmp_hw)
mac_service_map
end
end
end

View File

@ -53,7 +53,7 @@ module VagrantPlugins
def self.create_vm_interface_map(machine, guest_network)
if !machine.provider.capability?(:nic_mac_addresses)
raise Errors::CantReadMACAddresses,
raise Vagrant::Errors::CantReadMACAddresses,
provider: machine.provider_name.to_s
end

View File

@ -6,10 +6,6 @@ module VagrantPlugins
error_namespace("vagrant_windows.errors")
end
class CantReadMACAddresses < WindowsError
error_key(:cant_read_mac_addresses)
end
class NetworkWinRMRequired < WindowsError
error_key(:network_winrm_required)
end

View File

@ -603,6 +603,14 @@ en:
issues. The error from Bundler is:
%{message}
cant_read_mac_addresses: |-
The provider you are using ('%{provider}') doesn't support the
"nic_mac_addresses" provider capability which is required
for advanced networking to work with this guest OS. Please inform
the author of the provider to add this feature.
Until then, you must remove any networking configurations other
than forwarded ports from your Vagrantfile for Vagrant to continue.
capability_host_explicit_not_detected: |-
The explicit capability host specified of '%{value}' could not be
found.

View File

@ -1,14 +1,6 @@
en:
vagrant_windows:
errors:
cant_read_mac_addresses: |-
The provider being used to start Windows ('%{provider}')
doesn't support the "nic_mac_addresses" capability which is required
for advanced networking to work with Windows guests. Please inform
the author of the provider to add this feature.
Until then, you must remove any networking configurations other
than forwarded ports from your Vagrantfile for Vagrant to continue.
network_winrm_required: |-
Configuring networks on Windows requires the communicator to be
set to WinRM. To do this, add the following to your Vagrantfile: