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,45 +6,109 @@ module VagrantPlugins
module GuestDarwin module GuestDarwin
module Cap module Cap
class ConfigureNetworks class ConfigureNetworks
@@logger = Log4r::Logger.new("vagrant::guest::darwin::configure_networks")
include Vagrant::Util include Vagrant::Util
def self.configure_networks(machine, networks) def self.configure_networks(machine, networks)
# Slightly different than other plugins, using the template to build commands if !machine.provider.capability?(:nic_mac_addresses)
# rather than templating the files. raise Errors::CantReadMACAddresses,
provider: machine.provider_name.to_s
end
machine.communicate.sudo("networksetup -detectnewhardware") nic_mac_addresses = machine.provider.capability(:nic_mac_addresses)
machine.communicate.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces") @@logger.debug("mac addresses: #{nic_mac_addresses.inspect}")
tmpints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
machine.communicate.download("/tmp/vagrant.interfaces",tmpints)
devlist = [] mac_service_map = create_mac_service_map(machine)
ints = ::IO.read(tmpints)
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| ints.split(/\n\n/m).each do |i|
if i.match(/Hardware/) and not i.match(/Ethernet/).nil? if i.match(/Hardware/) && i.match(/Ethernet/)
devmap = {}
# Ethernet, should be 2 lines, # Ethernet, should be 2 lines,
# (3) Thunderbolt Ethernet # (3) Thunderbolt Ethernet
# (Hardware Port: Thunderbolt Ethernet, Device: en1) # (Hardware Port: Thunderbolt Ethernet, Device: en1)
# multiline, should match "Thunderbolt Ethernet", "en1" # multiline, should match "Thunderbolt Ethernet", "en1"
devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m) devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m)
devmap[:interface] = devicearry[2] service = devicearry[1]
devmap[:service] = devicearry[1] interface = devicearry[2]
devlist << devmap
# Should map interface to service { "en1" => "Thunderbolt Ethernet" }
interface_map[interface] = service
end end
end end
File.delete(tmpints) File.delete(tmp_ints)
networks.each do |network| mac_service_map = {}
service_name = devlist[network[:interface]][:service] macs = ::IO.read(tmp_hw)
if network[:type].to_sym == :static macs.split(/\n\n/m).each do |i|
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}" if i.match(/Hardware/) && i.match(/Ethernet/)
elsif network[:type].to_sym == :dhcp # Ethernet, should be 3 lines,
command = "networksetup -setdhcp \"#{service_name}\"" # 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 end
machine.communicate.sudo(command)
end end
File.delete(tmp_hw)
mac_service_map
end end
end end
end end