vagrant-windows 1.0.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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