guests/windows: configure networks

This commit is contained in:
Mitchell Hashimoto 2014-03-14 11:23:07 -07:00
parent d86a9ecade
commit d600e83e6d
9 changed files with 216 additions and 18 deletions

View File

@ -1,10 +1,6 @@
require "log4r" require "log4r"
require_relative '../../communication/guestnetwork' require_relative "../guest_network"
require_relative '../../communication/winrmshell'
require_relative '../../errors'
require_relative '../../helper'
require_relative '../../windows_machine'
module VagrantPlugins module VagrantPlugins
module GuestWindows module GuestWindows
@ -15,19 +11,20 @@ module VagrantPlugins
def self.configure_networks(machine, networks) def self.configure_networks(machine, networks)
@@logger.debug("Networks: #{networks.inspect}") @@logger.debug("Networks: #{networks.inspect}")
windows_machine = VagrantWindows::WindowsMachine.new(machine) guest_network = GuestNetwork.new(machine.communicate.shell)
guest_network = VagrantWindows::Communication::GuestNetwork.new(windows_machine.winrmshell) if machine.provider_name.to_s.start_with?("vmware")
if windows_machine.is_vmware?() machine.ui.warn("Configuring secondary network adapters through VMware ")
@@logger.warn('Configuring secondary network adapters through VMware is not yet supported.') machine.ui.warn("on Windows is not yet supported. You will need to manually")
@@logger.warn('You will need to manually configure the network adapter.') machine.ui.warn("configure the network adapter.")
else else
vm_interface_map = create_vm_interface_map(windows_machine, guest_network) vm_interface_map = create_vm_interface_map(machine, guest_network)
networks.each do |network| networks.each do |network|
interface = vm_interface_map[network[:interface]+1] interface = vm_interface_map[network[:interface]+1]
if interface.nil? if interface.nil?
@@logger.warn("Could not find interface for network #{network.inspect}") @@logger.warn("Could not find interface for network #{network.inspect}")
next next
end end
network_type = network[:type].to_sym network_type = network[:type].to_sym
if network_type == :static if network_type == :static
guest_network.configure_static_interface( guest_network.configure_static_interface(
@ -40,19 +37,27 @@ module VagrantPlugins
interface[:index], interface[:index],
interface[:net_connection_id]) interface[:net_connection_id])
else else
raise WindowsError, "#{network_type} network type is not supported, try static or dhcp" raise "#{network_type} network type is not supported, try static or dhcp"
end end
end end
end end
guest_network.set_all_networks_to_work() if windows_machine.windows_config.set_work_network
end
#{1=>{:name=>"Local Area Connection", :mac_address=>"0800275FAC5B", :interface_index=>"11", :index=>"7"}} if machine.config.windows.set_work_network
def self.create_vm_interface_map(windows_machine, guest_network) guest_network.set_all_networks_to_work
vm_interface_map = {} end
driver_mac_address = windows_machine.read_mac_addresses.invert end
def self.create_vm_interface_map(machine, guest_network)
if !machine.provider.capability?(:nic_mac_addresses)
raise Errors::CantReadMACAddresses,
provider: machine.provider_name.to_s
end
driver_mac_address = machine.provider.capability(:nic_mac_addresses)
@@logger.debug("mac addresses: #{driver_mac_address.inspect}") @@logger.debug("mac addresses: #{driver_mac_address.inspect}")
guest_network.network_adapters().each do |nic|
vm_interface_map = {}
guest_network.network_adapters.each do |nic|
@@logger.debug("nic: #{nic.inspect}") @@logger.debug("nic: #{nic.inspect}")
naked_mac = nic[:mac_address].gsub(':','') naked_mac = nic[:mac_address].gsub(':','')
if driver_mac_address[naked_mac] if driver_mac_address[naked_mac]
@ -63,6 +68,7 @@ module VagrantPlugins
:index => nic[:index] } :index => nic[:index] }
end end
end end
@@logger.debug("vm_interface_map: #{vm_interface_map.inspect}") @@logger.debug("vm_interface_map: #{vm_interface_map.inspect}")
vm_interface_map vm_interface_map
end end

View File

@ -0,0 +1,14 @@
module VagrantPlugins
module GuestWindows
module Errors
# A convenient superclass for all our errors.
class WindowsError < Vagrant::Errors::VagrantError
error_namespace("vagrant_windows.errors")
end
class CantReadMACAddresses < WindowsError
error_key(:cant_read_mac_addresses)
end
end
end
end

View File

@ -0,0 +1,125 @@
require "log4r"
module VagrantPlugins
module GuestWindows
# Manages the remote Windows guest network.
class GuestNetwork
PS_GET_WSMAN_VER = '((test-wsman).productversion.split(" ") | select -last 1).split("\.")[0]'
WQL_NET_ADAPTERS_V2 = 'SELECT * FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL'
def initialize(winrmshell)
@logger = Log4r::Logger.new("vagrant::windows::guestnetwork")
@winrmshell = winrmshell
end
# Returns an array of all NICs on the guest. Each array entry is a
# Hash of the NICs properties.
#
# @return [Array]
def network_adapters
wsman_version == 2? network_adapters_v2_winrm : network_adapters_v3_winrm
end
# Checks to see if the specified NIC is currently configured for DHCP.
#
# @return [Boolean]
def is_dhcp_enabled(nic_index)
has_dhcp_enabled = false
cmd = "Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter \"Index=#{nic_index} and DHCPEnabled=True\""
@winrmshell.powershell(cmd) do |type, line|
has_dhcp_enabled = !line.nil?
end
@logger.debug("NIC #{nic_index} has DHCP enabled: #{has_dhcp_enabled}")
has_dhcp_enabled
end
# Configures the specified interface for DHCP
#
# @param [Integer] The interface index.
# @param [String] The unique name of the NIC, such as 'Local Area Connection'.
def configure_dhcp_interface(nic_index, net_connection_id)
@logger.info("Configuring NIC #{net_connection_id} for DHCP")
if !is_dhcp_enabled(nic_index)
netsh = "netsh interface ip set address \"#{net_connection_id}\" dhcp"
@winrmshell.powershell(netsh)
end
end
# Configures the specified interface using a static address
#
# @param [Integer] The interface index.
# @param [String] The unique name of the NIC, such as 'Local Area Connection'.
# @param [String] The static IP address to assign to the specified NIC.
# @param [String] The network mask to use with the static IP.
def configure_static_interface(nic_index, net_connection_id, ip, netmask)
@logger.info("Configuring NIC #{net_connection_id} using static ip #{ip}")
#netsh interface ip set address "Local Area Connection 2" static 192.168.33.10 255.255.255.0
netsh = "netsh interface ip set address \"#{net_connection_id}\" static #{ip} #{netmask}"
@winrmshell.powershell(netsh)
end
# Sets all networks on the guest to 'Work Network' mode. This is
# to allow guest access from the host via a private IP on Win7
# https://github.com/WinRb/vagrant-windows/issues/63
def set_all_networks_to_work
@logger.info("Setting all networks to 'Work Network'")
command = File.read(File.expand_path("../scripts/set_work_network.ps1"))
@winrmshell.powershell(command)
end
protected
# Checks the WinRS version on the guest. Usually 2 on Windows 7/2008
# and 3 on Windows 8/2012.
#
# @return [Integer]
def wsman_version
@logger.debug("querying WSMan version")
version = ''
@winrmshell.powershell(PS_GET_WSMAN_VER) do |type, line|
version = version + "#{line}" if type == :stdout && !line.nil?
end
@logger.debug("wsman version: #{version}")
Integer(version)
end
# Returns an array of all NICs on the guest. Each array entry is a
# Hash of the NICs properties. This method should only be used on
# guests that have WinRS version 2.
#
# @return [Array]
def network_adapters_v2_winrm
@logger.debug("querying network adapters")
# Get all NICs that have a MAC address
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx
adapters = @winrmshell.wql(WQL_NET_ADAPTERS_V2)[:win32_network_adapter]
@logger.debug("#{adapters.inspect}")
return adapters
end
# Returns an array of all NICs on the guest. Each array entry is a
# Hash of the NICs properties. This method should only be used on
# guests that have WinRS version 3.
#
# This method is a workaround until the WinRM gem supports WinRS version 3.
#
# @return [Array]
def network_adapters_v3_winrm
command = File.read(File.expand_path("../scripts/winrs_v3_get_adapters.ps1"))
output = ""
@winrmshell.powershell(command) do |type, line|
output = output + "#{line}" if type == :stdout && !line.nil?
end
adapters = []
JSON.parse(output).each do |nic|
adapters << nic.inject({}){ |memo,(k,v)| memo[k.to_sym] = v; memo }
end
@logger.debug("#{adapters.inspect}")
return adapters
end
end
end
end

View File

@ -2,6 +2,8 @@ require "vagrant"
module VagrantPlugins module VagrantPlugins
module GuestWindows module GuestWindows
autoload :Errors, File.expand_path("../errors", __FILE__)
class Plugin < Vagrant.plugin("2") class Plugin < Vagrant.plugin("2")
name "Windows guest." name "Windows guest."
description "Windows guest support." description "Windows guest support."
@ -13,6 +15,7 @@ module VagrantPlugins
guest("windows") do guest("windows") do
require_relative "guest" require_relative "guest"
init!
Guest Guest
end end
@ -45,6 +48,16 @@ module VagrantPlugins
require_relative "cap/mount_shared_folder" require_relative "cap/mount_shared_folder"
Cap::MountSharedFolder Cap::MountSharedFolder
end end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path(
"templates/locales/guest_windows.yml", Vagrant.source_root)
I18n.reload!
@_init = true
end
end end
end end
end end

View File

@ -0,0 +1,6 @@
# Get network connections
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
$connections = $networkListManager.GetNetworkConnections()
# Set network location to Private for all networks
$connections | % {$_.GetNetwork().SetCategory(1)}

View File

@ -0,0 +1,11 @@
$adapters = get-ciminstance win32_networkadapter -filter "macaddress is not null"
$processed = @()
foreach ($adapter in $adapters) {
$Processed += new-object PSObject -Property @{
mac_address = $adapter.macaddress
net_connection_id = $adapter.netconnectionid
interface_index = $adapter.interfaceindex
index = $adapter.index
}
}
convertto-json -inputobject $processed

View File

@ -15,6 +15,13 @@ module VagrantPlugins
end end
end end
end end
# Reads the network interface card MAC addresses and returns them.
#
# @return [Hash<String, String>] Adapter => MAC address
def self.nic_mac_addresses(machine)
machine.provider.driver.read_mac_addresses
end
end end
end end
end end

View File

@ -28,6 +28,11 @@ module VagrantPlugins
require_relative "cap" require_relative "cap"
Cap Cap
end end
provider_capability(:virtualbox, :nic_mac_addresses) do
require_relative "cap"
Cap
end
end end
autoload :Action, File.expand_path("../action", __FILE__) autoload :Action, File.expand_path("../action", __FILE__)

View File

@ -0,0 +1,11 @@
en:
vagrant_winrm:
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.