From b81f430f31749edf9e9d1892ea0eea8e87228148 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 14 Mar 2014 10:51:00 -0700 Subject: [PATCH] guests/windows: initial vagrant-windows extraction --- .../guests/windows/cap/change_host_name.rb | 14 ++++ .../guests/windows/cap/configure_networks.rb | 72 +++++++++++++++++++ plugins/guests/windows/cap/halt.rb | 16 +++++ .../guests/windows/cap/mount_shared_folder.rb | 36 ++++++++++ plugins/guests/windows/config.rb | 32 +++++++++ plugins/guests/windows/guest.rb | 10 +++ plugins/guests/windows/plugin.rb | 50 +++++++++++++ .../windows/scripts/mount_volume.ps1.erb | 48 +++++++++++++ 8 files changed, 278 insertions(+) create mode 100644 plugins/guests/windows/cap/change_host_name.rb create mode 100644 plugins/guests/windows/cap/configure_networks.rb create mode 100644 plugins/guests/windows/cap/halt.rb create mode 100644 plugins/guests/windows/cap/mount_shared_folder.rb create mode 100644 plugins/guests/windows/config.rb create mode 100644 plugins/guests/windows/guest.rb create mode 100644 plugins/guests/windows/plugin.rb create mode 100644 plugins/guests/windows/scripts/mount_volume.ps1.erb diff --git a/plugins/guests/windows/cap/change_host_name.rb b/plugins/guests/windows/cap/change_host_name.rb new file mode 100644 index 000000000..5f39cd05f --- /dev/null +++ b/plugins/guests/windows/cap/change_host_name.rb @@ -0,0 +1,14 @@ +module VagrantPlugins + module GuestWindows + module Cap + module ChangeHostName + def self.change_host_name(machine, name) + # On windows, renaming a computer seems to require a reboot + machine.communicate.execute( + "wmic computersystem where name=\"%COMPUTERNAME%\" call rename name=\"#{name}\"", + :shell => :cmd) + end + end + end + end +end diff --git a/plugins/guests/windows/cap/configure_networks.rb b/plugins/guests/windows/cap/configure_networks.rb new file mode 100644 index 000000000..7188d7c55 --- /dev/null +++ b/plugins/guests/windows/cap/configure_networks.rb @@ -0,0 +1,72 @@ +require "log4r" + +require_relative '../../communication/guestnetwork' +require_relative '../../communication/winrmshell' +require_relative '../../errors' +require_relative '../../helper' +require_relative '../../windows_machine' + +module VagrantPlugins + module GuestWindows + module Cap + module ConfigureNetworks + @@logger = Log4r::Logger.new("vagrant::guest::windows::configure_networks") + + 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.') + else + vm_interface_map = create_vm_interface_map(windows_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( + interface[:index], + interface[:net_connection_id], + network[:ip], + network[:netmask]) + elsif network_type == :dhcp + guest_network.configure_dhcp_interface( + interface[:index], + interface[:net_connection_id]) + else + raise WindowsError, "#{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 + @@logger.debug("mac addresses: #{driver_mac_address.inspect}") + guest_network.network_adapters().each do |nic| + @@logger.debug("nic: #{nic.inspect}") + naked_mac = nic[:mac_address].gsub(':','') + if driver_mac_address[naked_mac] + vm_interface_map[driver_mac_address[naked_mac]] = { + :net_connection_id => nic[:net_connection_id], + :mac_address => naked_mac, + :interface_index => nic[:interface_index], + :index => nic[:index] } + end + end + @@logger.debug("vm_interface_map: #{vm_interface_map.inspect}") + vm_interface_map + end + end + end + end +end diff --git a/plugins/guests/windows/cap/halt.rb b/plugins/guests/windows/cap/halt.rb new file mode 100644 index 000000000..a5d6e2125 --- /dev/null +++ b/plugins/guests/windows/cap/halt.rb @@ -0,0 +1,16 @@ +module VagrantPlugins + module GuestWindows + module Cap + module Halt + def self.halt(machine) + # Fix vagrant-windows GH-129, if there's an existing scheduled + # reboot cancel it so shutdown succeeds + machine.communicate.execute("shutdown -a", error_check: false) + + # Force shutdown the machine now + machine.communicate.execute("shutdown /s /t 1 /c \"Vagrant Halt\" /f /d p:4:1") + end + end + end + end +end diff --git a/plugins/guests/windows/cap/mount_shared_folder.rb b/plugins/guests/windows/cap/mount_shared_folder.rb new file mode 100644 index 000000000..673b584f2 --- /dev/null +++ b/plugins/guests/windows/cap/mount_shared_folder.rb @@ -0,0 +1,36 @@ +require "vagrant/util/template_renderer" + +module VagrantPlugins + module GuestWindows + module Cap + class MountSharedFolder + def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) + mount_shared_folder(machine, name, guestpath, "\\\\vboxsrv\\") + end + + def self.mount_vmware_shared_folder(machine, name, guestpath, options) + mount_shared_folder(machine, name, guestpath, "\\\\vmware-host\\Shared Folders\\") + end + + def self.mount_parallels_shared_folder(machine, name, guestpath, options) + mount_shared_folder(machine, name, guestpath, "\\\\psf\\") + end + + protected + + def self.mount_shared_folder(machine, name, guestpath, vm_provider_unc_base) + name = name.gsub(/[\/\/]/,'_').sub(/^_/, '') + + path = File.expand_path("../../scripts/mount_volume.ps1.erb", __FILE__) + script = Vagrant::Util::TemplateRenderer.render(path, options: { + mount_point: guestpath, + share_name: name, + vm_provider_unc_path: vm_provider_unc_base + name, + }) + + machine.communicate.execute(script, shell: :powershell) + end + end + end + end +end diff --git a/plugins/guests/windows/config.rb b/plugins/guests/windows/config.rb new file mode 100644 index 000000000..6ac7d894a --- /dev/null +++ b/plugins/guests/windows/config.rb @@ -0,0 +1,32 @@ +module VagrantPlugins + module GuestWindows + class Config < Vagrant.plugin("2", :config) + attr_accessor :halt_timeout + attr_accessor :halt_check_interval + attr_accessor :set_work_network + + def initialize + @halt_timeout = UNSET_VALUE + @halt_check_interval = UNSET_VALUE + @set_work_network = UNSET_VALUE + end + + def validate(machine) + errors = [] + + errors << "windows.halt_timeout cannot be nil." if machine.config.windows.halt_timeout.nil? + errors << "windows.halt_check_interval cannot be nil." if machine.config.windows.halt_check_interval.nil? + + errors << "windows.set_work_network cannot be nil." if machine.config.windows.set_work_network.nil? + + { "Windows Guest" => errors } + end + + def finalize! + @halt_timeout = 30 if @halt_timeout == UNSET_VALUE + @halt_check_interval = 1 if @halt_check_interval == UNSET_VALUE + @set_work_network = false if @set_work_network == UNSET_VALUE + end + end + end +end diff --git a/plugins/guests/windows/guest.rb b/plugins/guests/windows/guest.rb new file mode 100644 index 000000000..97e607b05 --- /dev/null +++ b/plugins/guests/windows/guest.rb @@ -0,0 +1,10 @@ +module VagrantPlugins + module GuestWindows + class Guest < Vagrant.plugin("2", :guest) + def detect?(machine) + # See if the Windows directory is present. + machine.communicate.test("test -d $Env:SystemRoot") + end + end + end +end diff --git a/plugins/guests/windows/plugin.rb b/plugins/guests/windows/plugin.rb new file mode 100644 index 000000000..50f6df34e --- /dev/null +++ b/plugins/guests/windows/plugin.rb @@ -0,0 +1,50 @@ +require "vagrant" + +module VagrantPlugins + module GuestWindows + class Plugin < Vagrant.plugin("2") + name "Windows guest." + description "Windows guest support." + + config("windows") do + require_relative "config" + Config + end + + guest("windows") do + require_relative "guest" + Guest + end + + guest_capability(:windows, :change_host_name) do + require_relative "cap/change_host_name" + Cap::ChangeHostName + end + + guest_capability(:windows, :configure_networks) do + require_relative "cap/configure_networks" + Cap::ConfigureNetworks + end + + guest_capability(:windows, :halt) do + require_relative "cap/halt" + Cap::Halt + end + + guest_capability(:windows, :mount_virtualbox_shared_folder) do + require_relative "cap/mount_shared_folder" + Cap::MountSharedFolder + end + + guest_capability(:windows, :mount_vmware_shared_folder) do + require_relative "cap/mount_shared_folder" + Cap::MountSharedFolder + end + + guest_capability(:windows, :mount_parallels_shared_folder) do + require_relative "cap/mount_shared_folder" + Cap::MountSharedFolder + end + end + end +end diff --git a/plugins/guests/windows/scripts/mount_volume.ps1.erb b/plugins/guests/windows/scripts/mount_volume.ps1.erb new file mode 100644 index 000000000..b8112d2e3 --- /dev/null +++ b/plugins/guests/windows/scripts/mount_volume.ps1.erb @@ -0,0 +1,48 @@ +function Test-ReparsePoint([string]$path) { + $file = Get-Item $path -Force -ea 0 + return [bool]($file.Attributes -band [IO.FileAttributes]::ReparsePoint) +} + +$MountPoint = [System.IO.Path]::GetFullPath("<%= options[:mount_point] %>") +$ShareName = "<%= options[:share_name] %>" +$VmProviderUncPath = "<%= options[:vm_provider_unc_path] %>" + +# https://github.com/BIAINC/vagrant-windows/issues/4 +# Not sure why this works, but it does. + +& net use $ShareName 2>&1 | Out-Null + +Write-Debug "Attempting to mount $ShareName to $MountPoint" +if( (Test-Path "$MountPoint") -and (Test-ReparsePoint "$MountPoint") ) +{ + Write-Debug "Junction already exists, so I will delete it" + # Powershell refuses to delete junctions, oh well use cmd + cmd /c rd "$MountPoint" + + if ( $LASTEXITCODE -ne 0 ) + { + Write-Error "Failed to delete symbolic link at $MountPoint" + exit 1 + } + +} +elseif(Test-Path $MountPoint) +{ + Write-Debug "Mount point already exists and is not a symbolic link" + exit 1 +} + +$BaseDirectory = [System.IO.Path]::GetDirectoryName($MountPoint) + +if (-not (Test-Path $BaseDirectory)) +{ + Write-Debug "Creating parent directory for mount point $BaseDirectory" + New-Item $BaseDirectory -Type Directory -Force | Out-Null +} + +cmd /c mklink /D "$MountPoint" "$VmProviderUncPath" | out-null + +if ( $LASTEXITCODE -ne 0 ) +{ + exit 1 +}