vagrant-windows 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +1 -1
  5. data/README.md +77 -20
  6. data/Rakefile +14 -4
  7. data/lib/vagrant-windows/communication/guestnetwork.rb +133 -0
  8. data/lib/vagrant-windows/communication/winrmcommunicator.rb +56 -151
  9. data/lib/vagrant-windows/communication/winrmfinder.rb +45 -0
  10. data/lib/vagrant-windows/communication/winrmshell.rb +141 -0
  11. data/lib/vagrant-windows/config/windows.rb +4 -0
  12. data/lib/vagrant-windows/config/winrm.rb +1 -1
  13. data/lib/vagrant-windows/guest/cap/change_host_name.rb +14 -0
  14. data/lib/vagrant-windows/guest/cap/configure_networks.rb +69 -0
  15. data/lib/vagrant-windows/guest/cap/halt.rb +22 -0
  16. data/lib/vagrant-windows/guest/cap/mount_virtualbox_shared_folder.rb +17 -0
  17. data/lib/vagrant-windows/guest/cap/mount_vmware_shared_folder.rb +15 -0
  18. data/lib/vagrant-windows/guest/windows.rb +46 -77
  19. data/lib/vagrant-windows/helper.rb +6 -0
  20. data/lib/vagrant-windows/monkey_patches/{machine.rb → lib/vagrant/machine.rb} +7 -0
  21. data/lib/vagrant-windows/monkey_patches/plugins/providers/virtualbox/action/share_folders.rb +44 -0
  22. data/lib/vagrant-windows/monkey_patches/{vbox_42_driver.rb → plugins/providers/virtualbox/driver/version_4_2.rb} +0 -0
  23. data/lib/vagrant-windows/monkey_patches/plugins/provisioners/chef/provisioner/chef_client.rb +1 -0
  24. data/lib/vagrant-windows/monkey_patches/plugins/provisioners/chef/provisioner/chef_solo.rb +106 -0
  25. data/lib/vagrant-windows/monkey_patches/{puppet.rb → plugins/provisioners/puppet/provisioner/puppet.rb} +5 -4
  26. data/lib/vagrant-windows/monkey_patches/plugins/provisioners/puppet/provisioner/puppet_server.rb +1 -0
  27. data/lib/vagrant-windows/monkey_patches/{provisioner.rb → plugins/provisioners/shell/provisioner.rb} +4 -4
  28. data/lib/vagrant-windows/plugin.rb +54 -27
  29. data/lib/vagrant-windows/scripts/cheftask.ps1.erb +47 -0
  30. data/lib/vagrant-windows/scripts/cheftask.xml.erb +45 -0
  31. data/lib/vagrant-windows/scripts/cheftaskrun.ps1.erb +16 -0
  32. data/lib/vagrant-windows/scripts/command_alias.ps1 +4 -0
  33. data/lib/vagrant-windows/scripts/{mount_volume.ps1.erb → mount_volume.virtualbox.ps1.erb} +1 -1
  34. data/lib/vagrant-windows/scripts/mount_volume.vmware.ps1.erb +49 -0
  35. data/lib/vagrant-windows/scripts/set_work_network.ps1 +6 -0
  36. data/lib/vagrant-windows/scripts/winrs_v3_get_adapters.ps1 +11 -0
  37. data/lib/vagrant-windows/version.rb +1 -1
  38. data/spec/spec_helper.rb +14 -0
  39. data/spec/vagrant-windows/config_spec.rb +3 -4
  40. data/spec/vagrant-windows/guestnetwork_spec.rb +47 -0
  41. data/spec/vagrant-windows/helper_spec.rb +14 -3
  42. data/spec/vagrant-windows/winrmcommunicator_spec.rb +26 -0
  43. data/vagrant-windows.gemspec +33 -2
  44. metadata +100 -55
  45. data/lib/vagrant-windows/monkey_patches/chef_solo.rb +0 -61
  46. data/lib/vagrant-windows/scripts/ps_runas.ps1.erb +0 -56
@@ -0,0 +1,45 @@
1
+ require 'log4r'
2
+ require_relative '../errors'
3
+
4
+ module VagrantWindows
5
+ module Communication
6
+ class WinRMFinder
7
+
8
+ attr_reader :logger
9
+ attr_reader :machine
10
+
11
+ def initialize(machine)
12
+ @machine = machine
13
+ @logger = Log4r::Logger.new("vagrant_windows::communication::winrmfinder")
14
+ end
15
+
16
+ def winrm_host_address
17
+ # Get the SSH info for the machine, raise an exception if the
18
+ # provider is saying that SSH is not ready.
19
+ ssh_info = @machine.ssh_info
20
+ raise Vagrant::Errors::SSHNotReady if ssh_info.nil?
21
+ @logger.info("WinRM host: #{ssh_info[:host]}")
22
+ return ssh_info[:host]
23
+ end
24
+
25
+ def winrm_host_port
26
+ expected_guest_port = @machine.config.winrm.guest_port
27
+ @logger.debug("Searching for WinRM port: #{expected_guest_port.inspect}")
28
+
29
+ # Look for the forwarded port only by comparing the guest port
30
+ begin
31
+ @machine.provider.driver.read_forwarded_ports.each do |_, _, hostport, guestport|
32
+ return hostport if guestport == expected_guest_port
33
+ end
34
+ rescue NoMethodError => e
35
+ # VMWare provider doesn't support read_forwarded_ports
36
+ @logger.debug(e.message)
37
+ end
38
+
39
+ # We tried, give up and use the configured port as-is
40
+ @machine.config.winrm.port
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,141 @@
1
+ require 'timeout'
2
+ require 'log4r'
3
+ require 'winrm'
4
+ require 'vagrant/util/retryable'
5
+ require_relative '../errors'
6
+
7
+ module VagrantWindows
8
+ module Communication
9
+ class WinRMShell
10
+
11
+ include Vagrant::Util::Retryable
12
+
13
+ # These are the exceptions that we retry because they represent
14
+ # errors that are generally fixed from a retry and don't
15
+ # necessarily represent immediate failure cases.
16
+ @@exceptions_to_retry_on = [
17
+ HTTPClient::KeepAliveDisconnected,
18
+ WinRM::WinRMHTTPTransportError,
19
+ Errno::EACCES,
20
+ Errno::EADDRINUSE,
21
+ Errno::ECONNREFUSED,
22
+ Errno::ECONNRESET,
23
+ Errno::ENETUNREACH,
24
+ Errno::EHOSTUNREACH,
25
+ Timeout::Error
26
+ ]
27
+
28
+ attr_reader :logger
29
+ attr_reader :username
30
+ attr_reader :password
31
+ attr_reader :host
32
+ attr_reader :port
33
+ attr_reader :timeout_in_seconds
34
+ attr_reader :max_tries
35
+
36
+ def initialize(host, username, password, options = {})
37
+ @logger = Log4r::Logger.new("vagrant_windows::communication::winrmshell")
38
+ @logger.debug("initializing WinRMShell")
39
+
40
+ @host = host
41
+ @port = options[:port] || 5985
42
+ @username = username
43
+ @password = password
44
+ @timeout_in_seconds = options[:timeout_in_seconds] || 60
45
+ @max_tries = options[:max_tries] || 20
46
+ end
47
+
48
+ def powershell(command, &block)
49
+ execute_shell(command, :powershell, &block)
50
+ end
51
+
52
+ def cmd(command, &block)
53
+ execute_shell(command, :cmd, &block)
54
+ end
55
+
56
+ def wql(query)
57
+ execute_wql(query)
58
+ end
59
+
60
+ protected
61
+
62
+ def execute_shell(command, shell=:powershell, &block)
63
+ retryable(:tries => @max_tries, :on => @@exceptions_to_retry_on, :sleep => 10) do
64
+ @logger.debug("#{shell} executing:\n#{command}")
65
+ if shell.eql? :cmd
66
+ output = session.cmd(command) do |out, err|
67
+ block.call(:stdout, out) if block_given? && out
68
+ block.call(:stderr, err) if block_given? && err
69
+ end
70
+ elsif shell.eql? :powershell
71
+ output = session.powershell(command) do |out, err|
72
+ block.call(:stdout, out) if block_given? && out
73
+ block.call(:stderr, err) if block_given? && err
74
+ end
75
+ else
76
+ raise Errors::WinRMInvalidShell, :shell => shell
77
+ end
78
+ @logger.debug("Exit status: #{output[:exitcode].inspect}")
79
+ return output
80
+ end
81
+ rescue => e
82
+ handle_winrm_exception(e, shell, command)
83
+ end
84
+
85
+ def execute_wql(query)
86
+ retryable(:tries => @max_tries, :on => @@exceptions_to_retry_on, :sleep => 10) do
87
+ @logger.debug("#executing wql: #{query}")
88
+ output = session.wql(query)
89
+ @logger.debug("wql result: #{output.inspect}")
90
+ return output
91
+ end
92
+ rescue => e
93
+ handle_winrm_exception(e, :wql, query)
94
+ end
95
+
96
+ def handle_winrm_exception(winrm_exception, shell, command)
97
+ if winrm_exception.message.include?("401") # return a more specific auth error for 401 errors
98
+ raise Errors::WinRMAuthorizationError,
99
+ :user => @username,
100
+ :password => @password,
101
+ :endpoint => endpoint,
102
+ :message => winrm_exception.message
103
+ end
104
+ raise Errors::WinRMExecutionError,
105
+ :shell => shell,
106
+ :command => command,
107
+ :message => winrm_exception.message
108
+ end
109
+
110
+ def new_session
111
+ @logger.info("Attempting to connect to WinRM...")
112
+ @logger.info(" - Host: #{@host}")
113
+ @logger.info(" - Port: #{@port}")
114
+ @logger.info(" - Username: #{@username}")
115
+
116
+ client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, endpoint_options)
117
+ client.set_timeout(@timeout_in_seconds)
118
+ client.toggle_nori_type_casting(:off) #we don't want coersion of types
119
+ client
120
+ end
121
+
122
+ def session
123
+ @session ||= new_session
124
+ end
125
+
126
+ def endpoint
127
+ "http://#{@host}:#{@port}/wsman"
128
+ end
129
+
130
+ def endpoint_options
131
+ { :user => @username,
132
+ :pass => @password,
133
+ :host => @host,
134
+ :port => @port,
135
+ :operation_timeout => @timeout_in_seconds,
136
+ :basic_auth_only => true }
137
+ end
138
+
139
+ end #WinShell class
140
+ end
141
+ end
@@ -6,10 +6,12 @@ module VagrantWindows
6
6
 
7
7
  attr_accessor :halt_timeout
8
8
  attr_accessor :halt_check_interval
9
+ attr_accessor :set_work_network
9
10
 
10
11
  def initialize
11
12
  @halt_timeout = UNSET_VALUE
12
13
  @halt_check_interval = UNSET_VALUE
14
+ @set_work_network = UNSET_VALUE
13
15
  end
14
16
 
15
17
  def validate(machine)
@@ -18,12 +20,14 @@ module VagrantWindows
18
20
  errors << "windows.halt_timeout cannot be nil." if machine.config.windows.halt_timeout.nil?
19
21
  errors << "windows.halt_check_interval cannot be nil." if machine.config.windows.halt_check_interval.nil?
20
22
 
23
+ errors << "windows.set_work_network cannot be nil." if machine.config.windows.set_work_network.nil?
21
24
  { "Windows Guest" => errors }
22
25
  end
23
26
 
24
27
  def finalize!
25
28
  @halt_timeout = 30 if @halt_timeout == UNSET_VALUE
26
29
  @halt_check_interval = 1 if @halt_check_interval == UNSET_VALUE
30
+ @set_work_network = false if @set_work_network == UNSET_VALUE
27
31
  end
28
32
 
29
33
  end
@@ -42,7 +42,7 @@ module VagrantWindows
42
42
  @host = "localhost" if @host == UNSET_VALUE
43
43
  @port = 5985 if @port == UNSET_VALUE
44
44
  @guest_port = 5985 if @guest_port == UNSET_VALUE
45
- @max_tries = 12 if @max_tries == UNSET_VALUE
45
+ @max_tries = 20 if @max_tries == UNSET_VALUE
46
46
  @timeout = 1800 if @timeout == UNSET_VALUE
47
47
  end
48
48
 
@@ -0,0 +1,14 @@
1
+ module VagrantWindows
2
+ module Guest
3
+ module Cap
4
+ class ChangeHostName
5
+ def self.change_host_name(machine, name)
6
+ #### on windows, renaming a computer seems to require a reboot
7
+ machine.communicate.execute(
8
+ "wmic computersystem where name=\"%COMPUTERNAME%\" call rename name=\"#{name}\"",
9
+ :shell => :cmd)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ require 'log4r'
2
+ require_relative '../../communication/guestnetwork'
3
+ require_relative '../../communication/winrmshell'
4
+ require_relative '../../errors'
5
+
6
+ module VagrantWindows
7
+ module Guest
8
+ module Cap
9
+ class ConfigureNetworks
10
+
11
+ @@logger = Log4r::Logger.new("vagrant_windows::guest::cap::configure_networks")
12
+
13
+ def self.configure_networks(machine, networks)
14
+ @@logger.debug("networks: #{networks.inspect}")
15
+
16
+ guest_network = ::VagrantWindows::Communication::GuestNetwork.new(machine.communicate.winrmshell)
17
+
18
+ if (machine.provider_name != :vmware_fusion) && (machine.provider_name != :vmware_workstation)
19
+ vm_interface_map = create_vm_interface_map(machine, guest_network)
20
+ end
21
+
22
+ networks.each do |network|
23
+ interface = vm_interface_map[network[:interface]+1]
24
+ if interface.nil?
25
+ @@logger.warn("Could not find interface for network #{network.inspect}")
26
+ next
27
+ end
28
+ network_type = network[:type].to_sym
29
+ if network_type == :static
30
+ guest_network.configure_static_interface(
31
+ interface[:index],
32
+ interface[:net_connection_id],
33
+ network[:ip],
34
+ network[:netmask])
35
+ elsif network_type == :dhcp
36
+ guest_network.configure_dhcp_interface(
37
+ interface[:index],
38
+ interface[:net_connection_id])
39
+ else
40
+ raise WindowsError, "#{network_type} network type is not supported, try static or dhcp"
41
+ end
42
+ end
43
+ guest_network.set_all_networks_to_work() if machine.config.windows.set_work_network
44
+ end
45
+
46
+ #{1=>{:name=>"Local Area Connection", :mac_address=>"0800275FAC5B", :interface_index=>"11", :index=>"7"}}
47
+ def self.create_vm_interface_map(machine, guest_network)
48
+ vm_interface_map = {}
49
+ driver_mac_address = machine.provider.driver.read_mac_addresses.invert
50
+ @@logger.debug("mac addresses: #{driver_mac_address.inspect}")
51
+ guest_network.network_adapters().each do |nic|
52
+ @@logger.debug("nic: #{nic.inspect}")
53
+ naked_mac = nic[:mac_address].gsub(':','')
54
+ if driver_mac_address[naked_mac]
55
+ vm_interface_map[driver_mac_address[naked_mac]] = {
56
+ :name => nic[:net_connection_id],
57
+ :mac_address => naked_mac,
58
+ :interface_index => nic[:interface_index],
59
+ :index => nic[:index] }
60
+ end
61
+ end
62
+ @@logger.debug("vm_interface_map: #{vm_interface_map.inspect}")
63
+ vm_interface_map
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,22 @@
1
+ module VagrantWindows
2
+ module Guest
3
+ module Cap
4
+ class Halt
5
+ def self.halt(machine)
6
+ machine.communicate.execute("shutdown /s /t 1 /c \"Vagrant Halt\" /f /d p:4:1")
7
+
8
+ # Wait until the VM's state is actually powered off. If this doesn't
9
+ # occur within a reasonable amount of time (15 seconds by default),
10
+ # then simply return and allow Vagrant to kill the machine.
11
+ count = 0
12
+ while machine.state != :poweroff
13
+ count += 1
14
+
15
+ return if count >= machine.config.windows.halt_timeout
16
+ sleep machine.config.windows.halt_check_interval
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../../helper'
2
+
3
+ module VagrantWindows
4
+ module Guest
5
+ module Cap
6
+ class MountVirtualBoxSharedFolder
7
+
8
+ def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
9
+ share_name = VagrantWindows::Helper.win_friendly_share_id(name)
10
+ mount_script = VagrantWindows.load_script_template("mount_volume.virtualbox.ps1",
11
+ :options => {:mount_point => guestpath, :share_name => share_name})
12
+ machine.communicate.execute(mount_script, {:shell => :powershell})
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module VagrantWindows
2
+ module Guest
3
+ module Cap
4
+ class MountVMwareSharedFolder
5
+
6
+ def self.mount_vmware_shared_folder(machine, name, guestpath, options)
7
+ share_name = VagrantWindows::Helper.win_friendly_share_id(name)
8
+ mount_script = VagrantWindows.load_script_template("mount_volume.vmware.ps1",
9
+ :options => {:mount_point => guestpath, :share_name => share_name})
10
+ machine.communicate.execute(mount_script, {:shell => :powershell})
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,102 +1,71 @@
1
+ require "vagrant"
2
+ require "vagrant-windows/guest/cap/change_host_name"
3
+ require "vagrant-windows/guest/cap/configure_networks"
4
+ require "vagrant-windows/guest/cap/halt"
5
+ require "vagrant-windows/guest/cap/mount_virtualbox_shared_folder"
6
+ require "vagrant-windows/guest/cap/mount_vmware_shared_folder"
7
+
1
8
  module VagrantWindows
2
9
  module Guest
3
- # A general Vagrant system implementation for "windows".
4
- #
5
- # Contributed by Chris McClimans <chris@hippiehacker.org>
6
10
  class Windows < Vagrant.plugin("2", :guest)
7
11
 
12
+ # Vagrant 1.1.x compatibibility methods
13
+ # Implement the 1.1.x methods and call through to the new 1.2.x capabilities
14
+
8
15
  attr_reader :machine
9
16
 
10
- def initialize(machine)
11
- super(machine)
17
+ def initialize(machine = nil)
18
+ super(machine) unless machine == nil
12
19
  @machine = machine
13
- @logger = Log4r::Logger.new("vagrant_windows::guest::windows")
14
20
  end
15
-
21
+
16
22
  def change_host_name(name)
17
- @logger.info("change host name to: #{name}")
18
- #### on windows, renaming a computer seems to require a reboot
19
- @machine.communicate.execute(
20
- "wmic computersystem where name=\"%COMPUTERNAME%\" call rename name=\"#{name}\"",
21
- :shell => :cmd)
23
+ VagrantWindows::Guest::Cap::ChangeHostName.change_host_name(@machine, name)
22
24
  end
23
-
24
- # TODO: I am sure that ciphering windows versions will be important at some point
25
+
25
26
  def distro_dispatch
26
- @logger.info("distro_dispatch: windows")
27
27
  :windows
28
28
  end
29
-
29
+
30
30
  def halt
31
- @machine.communicate.execute("shutdown /s /t 1 /c \"Vagrant Halt\" /f /d p:4:1")
32
-
33
- # Wait until the VM's state is actually powered off. If this doesn't
34
- # occur within a reasonable amount of time (15 seconds by default),
35
- # then simply return and allow Vagrant to kill the machine.
36
- count = 0
37
- while @machine.state != :poweroff
38
- count += 1
39
-
40
- return if count >= @machine.config.windows.halt_timeout
41
- sleep @machine.config.windows.halt_check_interval
42
- end
31
+ VagrantWindows::Guest::Cap::Halt.halt(@machine)
43
32
  end
44
-
45
- def mount_shared_folder(name, guestpath, options)
46
- @logger.info("mount_shared_folder: #{name}")
47
- mount_script = VagrantWindows.load_script_template("mount_volume.ps1",
48
- :options => {:mount_point => guestpath, :name => name})
49
- @machine.communicate.execute(mount_script, {:shell => :powershell})
33
+
34
+ def mount_virtualbox_shared_folder(name, guestpath, options)
35
+ VagrantWindows::Guest::Cap::MountVirtualBoxSharedFolder.mount_virtualbox_shared_folder(
36
+ @machine, name, guestpath, options)
50
37
  end
51
38
 
52
- def mount_nfs(ip, folders)
53
- raise NotImplementedError, "Mounting NFS Shares on windows is not implemented"
54
- # TODO: Maybe check for nfs support on the guest, since its often
55
- # not installed by default
56
- #folders.each do |name, opts|
57
- # # Expand the guestpath, so we can handle things like "~/vagrant"
58
- # real_guestpath = expanded_guest_path(opts[:guestpath])
59
-
60
- # Do the actual creating and mounting
61
- # @machine.communicate.sudo("mkdir -p #{real_guestpath}")
62
- # @machine.communicate.sudo("mount -o vers=#{opts[:nfs_version]} #{ip}:'#{opts[:hostpath]}' #{real_guestpath}",
63
- # :error_class => LinuxError,
64
- # :error_key => :mount_nfs_fail)
65
- #end
39
+ def mount_vmware_shared_folder(name, guestpath, options)
40
+ VagrantWindows::Guest::Cap::MountVMwareBoxSharedFolder.mount_vmware_shared_folder(
41
+ @machine, name, guestpath, options)
66
42
  end
67
-
43
+
68
44
  def configure_networks(networks)
69
- @logger.info("configure_networks: #{networks.inspect}")
70
- driver_mac_address = @machine.provider.driver.read_mac_addresses.invert
71
-
72
- vm_interface_map = {}
73
-
74
- # NetConnectionStatus=2 -- connected
75
- wql = "SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionStatus=2"
76
- @machine.communicate.session.wql(wql)[:win32_network_adapter].each do |nic|
77
- naked_mac = nic[:mac_address].gsub(':','')
78
- if driver_mac_address[naked_mac]
79
- vm_interface_map[driver_mac_address[naked_mac]] =
80
- { :name => nic[:net_connection_id], :mac_address => naked_mac, :index => nic[:interface_index] }
81
- end
82
- end
45
+ VagrantWindows::Guest::Cap::ConfigureNetworks.configure_networks(@machine, networks)
46
+ end
47
+
48
+
49
+ # Vagrant 1.2.x compatibibility methods
50
+
51
+ def detect?(machine)
83
52
 
84
- networks.each do |network|
85
- netsh = "netsh interface ip set address \"#{vm_interface_map[network[:interface]+1][:name]}\" "
86
- if network[:type].to_sym == :static
87
- netsh = "#{netsh} static #{network[:ip]} #{network[:netmask]}"
88
- elsif network[:type].to_sym == :dhcp
89
- netsh = "#{netsh} dhcp"
90
- else
91
- raise WindowsError, "#{network[:type]} network type is not supported, try static or dhcp"
92
- end
93
- @machine.communicate.execute(netsh)
94
- end
95
-
96
- #netsh interface ip set address name="Local Area Connection" static 192.168.0.100 255.255.255.0 192.168.0.1 1
53
+ # uname -o | grep Solaris
54
+ # uname -s | grep 'Linux'
55
+ # uname -s | grep 'FreeBSD'
56
+ # cat /etc/redhat-release
57
+ # uname -s | grep 'OpenBSD'
58
+ # cat /etc/gentoo-release
59
+ # cat /proc/version | grep 'Debian'
60
+ # cat /etc/arch-release
61
+ # cat /proc/version | grep 'Ubuntu'
62
+ # cat /etc/SuSE-release
63
+ # cat /etc/pld-release
64
+ # grep 'Fedora release 1[678]' /etc/redhat-release
97
65
 
66
+ # see if the Windows directory is present
67
+ machine.communicate.test("test -d $Env:SystemRoot")
98
68
  end
99
-
100
69
  end
101
70
  end
102
71
  end