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_relative '../../communication/guestnetwork'
require_relative '../../communication/winrmshell'
require_relative '../../errors'
require_relative '../../helper'
require_relative '../../windows_machine'
require_relative "../guest_network"
module VagrantPlugins
module GuestWindows
@ -15,19 +11,20 @@ module VagrantPlugins
def self.configure_networks(machine, networks)
@@logger.debug("Networks: #{networks.inspect}")
windows_machine = VagrantWindows::WindowsMachine.new(machine)
guest_network = VagrantWindows::Communication::GuestNetwork.new(windows_machine.winrmshell)
if windows_machine.is_vmware?()
@@logger.warn('Configuring secondary network adapters through VMware is not yet supported.')
@@logger.warn('You will need to manually configure the network adapter.')
guest_network = GuestNetwork.new(machine.communicate.shell)
if machine.provider_name.to_s.start_with?("vmware")
machine.ui.warn("Configuring secondary network adapters through VMware ")
machine.ui.warn("on Windows is not yet supported. You will need to manually")
machine.ui.warn("configure the network adapter.")
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|
interface = vm_interface_map[network[:interface]+1]
if interface.nil?
@@logger.warn("Could not find interface for network #{network.inspect}")
next
end
network_type = network[:type].to_sym
if network_type == :static
guest_network.configure_static_interface(
@ -40,19 +37,27 @@ module VagrantPlugins
interface[:index],
interface[:net_connection_id])
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
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"}}
def self.create_vm_interface_map(windows_machine, guest_network)
vm_interface_map = {}
driver_mac_address = windows_machine.read_mac_addresses.invert
if machine.config.windows.set_work_network
guest_network.set_all_networks_to_work
end
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}")
guest_network.network_adapters().each do |nic|
vm_interface_map = {}
guest_network.network_adapters.each do |nic|
@@logger.debug("nic: #{nic.inspect}")
naked_mac = nic[:mac_address].gsub(':','')
if driver_mac_address[naked_mac]
@ -63,6 +68,7 @@ module VagrantPlugins
:index => nic[:index] }
end
end
@@logger.debug("vm_interface_map: #{vm_interface_map.inspect}")
vm_interface_map
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 GuestWindows
autoload :Errors, File.expand_path("../errors", __FILE__)
class Plugin < Vagrant.plugin("2")
name "Windows guest."
description "Windows guest support."
@ -13,6 +15,7 @@ module VagrantPlugins
guest("windows") do
require_relative "guest"
init!
Guest
end
@ -45,6 +48,16 @@ module VagrantPlugins
require_relative "cap/mount_shared_folder"
Cap::MountSharedFolder
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

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
# 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

View File

@ -28,6 +28,11 @@ module VagrantPlugins
require_relative "cap"
Cap
end
provider_capability(:virtualbox, :nic_mac_addresses) do
require_relative "cap"
Cap
end
end
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.