Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VMWare Workstation Secondary Network Adapters on Windows #5000

Closed
uchagani opened this issue Dec 16, 2014 · 11 comments
Closed

VMWare Workstation Secondary Network Adapters on Windows #5000

uchagani opened this issue Dec 16, 2014 · 11 comments

Comments

@uchagani
Copy link
Contributor

When a secondary NIC is configured for VMWare Vagrant issues the following error:

==> default: Configuring secondary network adapters through VMware
==> default: on Windows is not yet supported. You will need to manually
==> default: configure the network adapter.

However, I can still configure a secondary NIC by doing:

  config.vm.provider "vmware_workstation" do |vmware|
    vmware.vmx["ethernet1.connectionType"] = "bridged"
    vmware.vmx["ethernet1.present"] = "TRUE"
    vmware.vmx["ethernet1.virtualDev"] = "e1000e"     
  end  

Why doesn't Vagrant configure this by default when these same values are present in the vmx file?

Base Box VMX File
ethernet0.addresstype = "generated"
ethernet0.bsdname = "en0"
ethernet0.connectiontype = "nat"
ethernet0.displayname = "Ethernet"
ethernet0.linkstatepropagation.enable = "FALSE"
ethernet0.pcislotnumber = "33"
ethernet0.present = "TRUE"
ethernet0.virtualdev = "e1000"
ethernet0.wakeonpcktrcv = "FALSE"
ethernet1.connectiontype = "bridged"
ethernet1.present = "true"
ethernet1.virtualdev = "e1000e"
@StefanScherer
Copy link
Contributor

To set the IP address for a private network, maybe this could help: http://professionalvmware.com/2008/12/vmware-vix-changing-ips-of-a-guest-vm/

vmrun -gu [Guest User] -gp [Guest Password] runProgramInGuest “[Path] to/the.vmx” c:\windows\system32\netsh int ip set address “Local Area Connection” static 192.168.15.25 255.255.255.0 192.168.15.1

@StefanScherer
Copy link
Contributor

@mitchellh I just made a test from my Mac spinning up a Windows 10 VM in VMware Fusion with an additional private network with a static ip 192.168.33.15 defined in the Vagrantfile:

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "windows_10"
  config.vm.network :forwarded_port, guest: 5985, host: 5985, id: "winrm", auto_correct: true
  config.vm.network :forwarded_port, guest: 3389, host: 3389, id: "rdp", auto_correct: true

  config.vm.network :private_network, ip: "192.168.33.15", gateway: "192.168.33.1"

  config.vm.communicator = "winrm"

  config.winrm.username = "vagrant"
  config.winrm.password = "vagrant"

  config.vm.guest = :windows
  config.windows.halt_timeout = 15

  ["vmware_fusion", "vmware_workstation"].each do |provider|
    config.vm.provider provider do |v, override|
      v.gui = true
    end
  end
end

Spinning up the Vagrant VM shows the warning for that second network card:

$ vagrant up --provider vmware_fusion
Bringing machine 'default' up with 'vmware_fusion' provider...
==> default: Cloning VMware VM: 'windows_10'. This can take some time...
==> default: Verifying vmnet devices are healthy...
==> default: Preparing network adapters...
==> default: Starting the VMware VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: WinRM address: 192.168.254.134:5985
    default: WinRM username: vagrant
    default: WinRM transport: plaintext
==> default: Machine booted and ready!
==> default: Forwarding ports...
    default: -- 3389 => 3389
    default: -- 22 => 2222
    default: -- 5985 => 5985
==> default: Configuring network adapters within the VM...
==> default: Configuring secondary network adapters through VMware 
==> default: on Windows is not yet supported. You will need to manually
==> default: configure the network adapter.
==> default: Enabling and configuring shared folders...
    default: -- /Users/stefan/code/win10: /vagrant

Inside the Windows 10 VM the network addresses are configured as follows:

C:\Users\vagrant>netsh int ip show addresses

Configuration for interface "Ethernet 3"
    DHCP enabled:                         Yes
    IP Address:                           192.168.254.134
    Subnet Prefix:                        192.168.254.0/24 (mask 255.255.255.0)
    Default Gateway:                      192.168.254.2
    Gateway Metric:                       1
    InterfaceMetric:                      5

Configuration for interface "Ethernet 2"
    DHCP enabled:                         Yes
    IP Address:                           192.168.33.129
    Subnet Prefix:                        192.168.33.0/24 (mask 255.255.255.0)
    InterfaceMetric:                      10

Configuration for interface "Loopback Pseudo-Interface 1"
    DHCP enabled:                         No
    IP Address:                           127.0.0.1
    Subnet Prefix:                        127.0.0.0/8 (mask 255.0.0.0)
    InterfaceMetric:                      50

The second network has DHCP enabled and has to be changed to the fixed IP address given in the Vagrantfile. I tried the vmrun command like above to do this and run this command from my MBP:

$ "/Applications/VMware Fusion.app/Contents/Library/vmrun" -gu vagrant -gp vagrant runProgramInGuest .vagrant/machines/default/vmware_fusion/d254df82-2549-48df-a691-36b2c5f56598/packer-vmware-iso.vmx netsh.exe int ip set address "Ethernet 2" static 192.168.33.15 255.255.255.0 192.168.33.1

Afterwards the networks in the Windows 10 VM shows the correct settings:

C:\Users\vagrant>netsh int ip show addresses

Configuration for interface "Ethernet 3"
    DHCP enabled:                         Yes
    IP Address:                           192.168.254.134
    Subnet Prefix:                        192.168.254.0/24 (mask 255.255.255.0)
    Default Gateway:                      192.168.254.2
    Gateway Metric:                       1
    InterfaceMetric:                      5

Configuration for interface "Ethernet 2"
    DHCP enabled:                         No
    IP Address:                           192.168.33.15
    Subnet Prefix:                        192.168.33.0/24 (mask 255.255.255.0)
    Default Gateway:                      192.168.33.1
    Gateway Metric:                       1
    InterfaceMetric:                      10

Configuration for interface "Loopback Pseudo-Interface 1"
    DHCP enabled:                         No
    IP Address:                           127.0.0.1
    Subnet Prefix:                        127.0.0.0/8 (mask 255.0.0.0)
    InterfaceMetric:                      50

The only difficulty with this approach is to get the interface name "Ethernet 2" from the host as it seems that vmrun doesn't show the stdout of the program in the VM.

But this could also be done with the winrm communicator to run the netsh.exe int ip addresses command.

I've tried it in a small provision script and came up to this workaround:

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "windows_10"
  config.vm.network :forwarded_port, guest: 5985, host: 5985, id: "winrm", auto_correct: true
  config.vm.network :forwarded_port, guest: 3389, host: 3389, id: "rdp", auto_correct: true

  config.vm.network :private_network, ip: "192.168.33.15", gateway: "192.168.33.1"

  config.vm.communicator = "winrm"

  config.winrm.username = "vagrant"
  config.winrm.password = "vagrant"

  config.vm.guest = :windows
  config.windows.halt_timeout = 15

  config.vm.provision "shell", inline: <<-SHELL
    netsh.exe int ip show addresses
    netsh.exe int ip set address "Ethernet 2" static 192.168.33.15 255.255.255.0 192.168.33.1
  SHELL

  ["vmware_fusion", "vmware_workstation"].each do |provider|
    config.vm.provider provider do |v, override|
      v.gui = true
    end
  end
end

Can this small part be done by the vagrant-vmware-fusion as well?
So for a user of Vagrant it just behaves like the virtualbox provider then.

@defn
Copy link

defn commented Jan 10, 2016

Is the blocker here the lack of nic_mac_addresses capability in the vmware provider?

@bsuh
Copy link

bsuh commented Jan 13, 2016

This should be relabeled guest/windows from host/windows.

It's disappointing that paid products like the vmware providers are less featured than their free open-source counterpart. And the problem has been reported...over a year ago...with no response.

The whole experience of using open-source vagrant with closed-source vmware providers is frustrating. You find a bug in vagrant and want to fix it? Ok but good luck getting your self-compiled vagrant to work with your vmware provider because of vagrant's "DRM". I feel like a less empowered second-rate citizen as a paying customer than people who are using only the free open-source parts of vagrant.

@bsuh
Copy link

bsuh commented Jan 13, 2016

diff --git a/plugins/guests/windows/cap/configure_networks.rb b/plugins/guests/windows/cap/configure_networks.rb
index a4f4685..d628f99 100644
--- a/plugins/guests/windows/cap/configure_networks.rb
+++ b/plugins/guests/windows/cap/configure_networks.rb
@@ -16,33 +16,27 @@ module VagrantPlugins
           @@logger.debug("Networks: #{networks.inspect}")

           guest_network = GuestNetwork.new(machine.communicate)
-          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(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
+          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(
-                  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 "#{network_type} network type is not supported, try static or dhcp"
-              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 "#{network_type} network type is not supported, try static or dhcp"
             end
           end

diff --git a/plugins/guests/windows/cap/nic_mac_addresses.rb b/plugins/guests/windows/cap/nic_mac_addresses.rb
new file mode 100644
index 0000000..d866ba2
--- /dev/null
+++ b/plugins/guests/windows/cap/nic_mac_addresses.rb
@@ -0,0 +1,41 @@
+require "log4r"
+
+module VagrantPlugins
+  module GuestWindows
+    module Cap
+      class NicMacAddresses
+        @@logger = Log4r::Logger.new("vagrant::guest::windows::cap::nic_mac_addresses")
+
+        def self.read_variable(machine, variable)
+          @@logger.debug("driver methods: #{machine.provider.driver.methods}")
+          result = machine.provider.driver.send(:vmrun, "readVariable", machine.id, "runtimeConfig", variable, retryable: true)
+          @@logger.debug("vmrun returned: #{result.exit_code} #{result.stdout} #{result.stderr}")
+          result.stdout.strip
+        end
+
+        def self.clean_mac(mac_address)
+          mac_address.gsub(':', '').upcase
+        end
+
+        def self.nic_mac_addresses(machine)
+          macs = []
+          i = 0
+          while true do
+            type = read_variable(machine, "ethernet#{i}.addressType")
+            case type
+            when "static"
+              macs.push(clean_mac(read_variable(machine, "ethernet#{i}.address")))
+            when "generated"
+              macs.push(clean_mac(read_variable(machine, "ethernet#{i}.generatedAddress")))
+            else
+              break
+            end
+            i += 1
+          end
+
+          Hash[macs.map.with_index{ |mac, index| [index+1, mac] }]
+        end
+      end
+    end
+  end
+end
diff --git a/plugins/guests/windows/plugin.rb b/plugins/guests/windows/plugin.rb
index d22dc27..d3d1d35 100644
--- a/plugins/guests/windows/plugin.rb
+++ b/plugins/guests/windows/plugin.rb
@@ -74,6 +74,11 @@ module VagrantPlugins
         Cap::RSync
       end

+      provider_capability(:vmware_fusion, :nic_mac_addresses) do
+        require_relative "cap/nic_mac_addresses"
+        Cap::NicMacAddresses
+      end
+
       protected

       def self.init!

Well after a few hours of tinkering, I was able to add the :nic_mac_addresses capability to the vmware_fusion provider by hackily patching stuff up. This diff can be applied to /opt/vagrant/embedded/gems/gems/vagrant-1.8.1/. But now it seems that configuring network adapters messes up the WinRM connection, but vagrant doesn't detect anything is out of order, so it just hangs waiting for the script output.

@StefanScherer
Copy link
Contributor

@bsuh Wow, great approach. Might be some inspiration for the Hashicorp team 🙏
I have another issue that holds me from updating to 1.8.1 (again) but then I'll have a look at it.

@bsuh
Copy link

bsuh commented Jan 13, 2016

diff --git a/plugins/communicators/winrm/communicator.rb b/plugins/communicators/winrm/communicator.rb
index e22a4f9..c39fb6b 100644
--- a/plugins/communicators/winrm/communicator.rb
+++ b/plugins/communicators/winrm/communicator.rb
@@ -102,7 +102,7 @@ module VagrantPlugins
         @logger.info("Checking whether WinRM is ready...")

         result = Timeout.timeout(@machine.config.winrm.timeout) do
-          shell(true).powershell("hostname")
+          shell().powershell("hostname")
         end

         @logger.info("WinRM is ready!")

Fixed hanging while configuring network adapters inside the VM. At first, vagrant tries to connect to WinRM on the IP address of the first ethernet interface, but later tries to connect to WinRM on the IP address of the second ethernet interface to set the static ip address. Setting the static ip address of the interface that is holding the WinRM connection destroys the WinRM connection causing it to hang. Instead of always requesting the WinRM address to connect to using :winrm_info, I made it reuse previous WinRM shell.

@akappel
Copy link

akappel commented Feb 26, 2016

+1 for getting this enhancement into Vagrant. I've attempted to add

config.vm.provider "vmware_workstation" do |vmware|
    vmware.vmx["ethernet1.connectionType"] = "bridged"
    vmware.vmx["ethernet1.present"] = "TRUE"
    vmware.vmx["ethernet1.virtualDev"] = "e1000e"     
  end

and receive the same message. RDPing into the machine shows that it has not received an extra, DHCP'd address as expected. Manually configuring bridged mode after a vagrant up seems like a hassle in the face of the ease of performing this action with VirtualBox.

@sethvargo sethvargo added the bug label Jul 18, 2016
@chrisroberts chrisroberts added this to the 1.9 milestone Sep 30, 2016
@StefanScherer
Copy link
Contributor

To patch the IP address of a second network card I wrote a small PowerShell script

    cfg.vm.network :private_network, ip: "192.168.33.2", gateway: "192.168.33.1"
    ["vmware_fusion", "vmware_workstation"].each do |provider|
      cfg.vm.provision "shell", path: "scripts/fix-second-network.ps1", privileged: false, args: "192.168.33.2"
    end

and the script looks like this. It searches for the network interface with the given subnet and sets the fixed IP address. This script might be a basis for a PR to get this into Vagrants core.

param ([String] $ip)

$subnet = $ip -replace "\.\d+$", ""

$name = (Get-NetIPAddress -AddressFamily IPv4 `
   | Where-Object -FilterScript { ($_.IPAddress).StartsWith($subnet) } `
   ).InterfaceAlias

if ($name) {
  Write-Host "Set IP address to $ip of interface $name"
  & netsh.exe int ip set address "$name" static $ip 255.255.255.0 "$subnet.1"
}

@lmayorga1980
Copy link

@StefanScherer I am trying to do the same thing as the remote vmware ESXI provider with a Windows Guest. Couldn't find the right Autounattend.xml syntax to set a static ip so now trying with netsh.

@goodwinb99
Copy link

Any of the examples here for using a provisioning script to set the interface configuration didn't work for me - it causes the provisioning step to hang. This is what I ended up doing as the last step. Note that even calling Start-ScheduledTask would cause the hang because the network config would be set while WinRM was connected. This step must be last for the same reason. If you need the IP to set for provisioning to continue, I suspect you could set the schedule task to start in 5 seconds, sleep for 10, and then continue. Hope it helps!

    config.vm.provision "shell", privileged: false, name: "Schedule setting secondary interface", inline: <<-SHELL.gsub(/^ +/, '')
      UnRegister-ScheduledTask SetIPConfig -EA SilentlyContinue
      $A = New-ScheduledTaskAction -Execute "netsh.exe" -Argument "int ip set address Ethernet1 static 192.168.198.10 255.255.255.0"
      $T = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1)
      $D = New-ScheduledTask -Action $A -Trigger $T
      Register-ScheduledTask SetIPConfig -InputObject $D
    SHELL

@uchagani uchagani closed this as not planned Won't fix, can't repro, duplicate, stale Sep 3, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants