guests/darwin: Configure network following the MAC addresses matching

Currently `configure_networks` guest cap configures NICs following the device order and fails
when the device order is mixed. We should detect the appropriate NIC by its MAC address.
This commit is contained in:
Mikhail Zholobov 2015-10-09 14:54:10 +03:00
parent 36cfc77167
commit e426455309
1 changed files with 86 additions and 22 deletions

View File

@ -6,46 +6,110 @@ 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 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
end
end
File.delete(tmpints)
service = devicearry[1]
interface = devicearry[2]
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}\""
# Should map interface to service { "en1" => "Thunderbolt Ethernet" }
interface_map[interface] = service
end
end
File.delete(tmp_ints)
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
machine.communicate.sudo(command)
# Should map MAC to service, { "00C100A1B2C3" => "Thunderbolt Ethernet" }
mac_service_map[naked_mac] = interface_map[interface]
end
end
File.delete(tmp_hw)
mac_service_map
end
end
end
end