Repository: hashicorp/vagrant (Ruby)

Issue:
Vagrant up fails when using VM snapshots with VB 7.0 
### Debug output

> vagrant up 
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.
Command: ["modifyvm", "de8f03f8-3b2e-4de9-acb7-b55669b5faf7", "--natpf1", "delete", "ssh", "--natpf1", "delete", "ssh"]
Stderr: VBoxManage.exe: error: Code E_INVALIDARG (0x80070057) (extended info not available)
VBoxManage.exe: error: Context: "RemoveRedirect(Bstr(ValueUnion.psz).raw())" at line 2108 of file VBoxManageModifyVM.cpp

### Expected behavior
Vagrant up should not stop with errors when clearing forwarded ports.

### Actual behavior
When vagrant up tries to clear any forwarded ports, it lists the forwarded ports and the same port gets duplicated which causes the issue.
read_forwarded_port() output:
DEBUG virtualbox_7_0: - [1, “ssh”, 2222, 22, “127.0.0.1”]
DEBUG virtualbox_7_0: - [1, “ssh”, 2222, 22, “127.0.0.1”]

See more troubleshooting details here:  
https://discuss.hashicorp.com/t/vbox-vm-with-vagrant-stopped-working/45826/4

The issue has to do with the routine read_forwarded_port(). It needs to be revisited to account for VB VM that makes use of Snapshots not to have the same forwarded port reported multiple times.
hashicorp/vagrant/blob/8549450cff40a55ec193540a5387a09caee309b4/plugins/providers/virtualbox/driver/version_7_0.rb#L224

### Reproduction information 

#### Vagrant version

vagrant 2.3.4

#### Host operating system

Windows 10 Enterprise
**VirtualBox Version 7.0.0 r153978 (Qt5.15.2)**

#### Steps to reproduce

1. Create a VM with vagrant up 
2. Vagrant halt
3. **Take a snapshot of the VM with VB gui** (--> that would add a new section in the .vbox config file that duplicates the forwarded port)
4. vagrant up

#### Vagrantfile
Here is the Vagrantfile but 


require_relative 'devenv'

if not is_valid_environment()
  exit
end

settings = load_settings('vagrant.yml')

box_disk_size_gb = settings['vm']['disksize']

fw_home = settings['framework']['home']
ansible_template = "#{fw_home}/template"
vb_name = File.basename(Dir.getwd)

Vagrant.configure("2") do |config|
  config.ssh.insert_key = false
  config.ssh.forward_agent = true
  config.ssh.forward_x11 = true

  config.vm.hostname = vb_name
  config.vm.box = settings['framework']['box_name']
  if settings['framework']['box_url'] != ""
    config.vm.box_url = settings['framework']['box_url']
  end
  config.vm.box_check_update = false

   if Vagrant.has_plugin?("vagrant-vbguest")
     config.vbguest.auto_update = false  
   end
   config.vm.synced_folder ".", "/vagrant"
  
  config.vm.synced_folder ansible_template, "/template"
  (settings['ssh']['dir'] != nil) && (config.vm.synced_folder settings['ssh']['dir'], "/ssh_config")

  settings['vm']['mounts'].each do |mount|
    config.vm.synced_folder mount[0], mount[1], mount_options: [ "dmode=777" ]
  end

  settings['vm']['ports'].each do |port|
    config.vm.network "forwarded_port", guest: port[0], host: port[1]
  end

  config.disksize.size = "#{box_disk_size_gb}GB"

  config.vm.provider "virtualbox" do |vb|
    vb.gui = settings['vm']['gui']
    vb.name = vb_name
    vb.memory = settings['vm']['memory']
    vb.cpus = settings['vm']['cpus']
    vb.customize ["modifyvm", :id, "--usbxhci", "off"]
    vb.customize ["modifyvm", :id, "--accelerate3d", "on"]
    vb.customize ["modifyvm", :id, "--vram", "64"]
    vb.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
    vb.customize ["modifyvm", :id, "--cpuexecutioncap", settings['vm']['cpuexecutioncap']]
  end

  config.vm.provision "ansible_local" do |ansible|
    ansible.compatibility_mode = "2.0"
    ansible.config_file = "/template/ansible.cfg"
    ansible.install_mode = 'pip_args_only'
    ansible.pip_install_cmd = "sudo apt-get install -y python3-pip python-is-python3 haveged && sudo ln -s -f /usr/bin/pip3 /usr/bin/pip"
    ansible.pip_args = "ansible==2.10.7"
    ansible.playbook = "playbook.yml"
    ansible.raw_arguments = ['--diff']
  end
end

### Workaround (temporary)
Change definition of read_forwarded_ports() in C:\HashiCorp\Vagrant\embedded\gems\2.3.4\gems\vagrant-2.3.4\plugins\providers\virtualbox\driver\version_7_0.rb

   def read_forwarded_ports(uuid=nil, active_only=false)
      return super
   end
Note: This workaround might not work with initial release of VB 7.0



Discussion:
Hey there, I'm unable to reproduce this issue. The Vagrantfile provided has lots of missing information, for example the 

```
settings['vm']['ports'].each do |port|
config.vm.network "forwarded_port", guest: port[0], host: port[1]
end
```

part makes is ambiguous as to how many forwarded ports you have. Could you provide the smallest possible Vagrantfile that still causes this issue. It would also be helpful if that Vagrantfile did not have any templating. Thanks! 
Sorry  I thought the Vagrantfile was not relevant but here is a simplified file that I just tried and I reproduced the issue (of course I had to revert my temporary fix describe above in OP). there is no other port forwarded than the SSH port.

```
vb_name = "vg.issue.13044"

Vagrant.configure("2") do |config|
  config.ssh.insert_key = false
  config.ssh.forward_agent = true
  config.ssh.forward_x11 = true

  config.vm.hostname = vb_name
  config.vm.box = 'ubuntu/jammy64'
  config.vm.box_check_update = false
  config.vbguest.auto_update = false
  config.disksize.size = "20GB"

   # Configure the VirtualBox provider
  config.vm.provider "virtualbox" do |vb|
    # Display the VirtualBox GUI when booting the machine
    vb.gui = true
    vb.name = vb_name
    vb.memory = 12000
    vb.cpus = 4

    vb.customize ["modifyvm", :id, "--usbxhci", "off"]
    vb.customize ["modifyvm", :id, "--accelerate3d", "on"]
    vb.customize ["modifyvm", :id, "--vram", "64"]
    vb.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
  end
end

```
and here are the logs 

```
-> vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/jammy64'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: vg.issue.13044
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2200 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Disk cannot be decreased in size. 20480 MB requested but disk is already 40960 MB.
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection aborted. Retrying...
==> default: Machine booted and ready!¬¬¬
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default:
    default: Guest Additions Version: 6.0.0 r127566
    default: VirtualBox Version: 7.0
==> default: Setting hostname...
==> default: Mounting shared folders...
    default: /vagrant => C:/Users/[…] vg_13044

-> vagrant halt
==> default: Attempting graceful shutdown of VM...

******************************************************************
** TAG VM with VB UI** (VERY IMPORTANT TO REPRODUCE)
******************************************************************

-> vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["modifyvm", "8e7f3d93-a867-4148-acfa-3824e2a89106", "--natpf1", "delete", "ssh", "--natpf1", "delete", "ssh"]

Stderr: VBoxManage.exe: error: Code E_INVALIDARG (0x80070057) (extended info not available)
VBoxManage.exe: error: Context: "RemoveRedirect(Bstr(ValueUnion.psz).raw())" at line 2108 of file VBoxManageModifyVM.cpp
`
```

I hope this helps to reproduce.


One more thing, you definitely want to look at the Virtualbox VM.box file that is being parsed by Vagrant to find the forwarded ports. 
Mine looks like something that. Pay attention the "Network" section is duplicated ... one for the VM and one for the snapshot. To fix the issue, the parsing function should only consider one single "Network" section (ignore any "Network" section that belongs to a Snapshot).

```
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.19-windows">
  <Machine uuid="{8e7f3d93-....-....-......-3824e2a89106}" name="vg.issue.13044" OSType="Ubuntu_64" currentSnapshot="{7fac5d59-304f-4f3e-ba35-93d764e8e267}" snapshotFolder="Snapshots" lastStateChange="2023-01-12T15:29:05Z">
    <MediaRegistry>
      <HardDisks>
        </HardDisk>
         ...        
        </HardDisk>
      </HardDisks>
    </MediaRegistry>
    <ExtraData>
      <ExtraDataItem name="GUI/LastGuestSizeHint" value="800,600"/>
      <ExtraDataItem name="GUI/LastNormalWindowPosition" value="640,279,800,641"/>
      <ExtraDataItem name="VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant" value="1"/>
    </ExtraData>
    <Snapshot uuid="{7fac5d59-304f-4f3e-ba35-93d764e8e267}" name="SNAPSHOT-FOR-VG-ISSUE-13044" timeStamp="2023-01-12T15:26:58Z">
      <Hardware>
        <CPU count="4">
          <PAE enabled="true"/>
          <LongMode enabled="true"/>
          <X2APIC enabled="true"/>
          <HardwareVirtExLargePages enabled="true"/>
        </CPU>
        <Memory RAMSize="12000"/>
        <Display VRAMSize="64" accelerate3D="true"/>
        <BIOS>
        ....
        </BIOS>
        <Network>
          <Adapter slot="0" enabled="true" MACAddress="..." type="...">
            <DisabledModes>
              <BridgedInterface name="Intel(R) Wi-Fi 6 AX201 160MHz"/>
              <InternalNetwork name="intnet"/>
              <NATNetwork name="NatNetwork"/>
            </DisabledModes>
            <NAT>
              <DNS use-proxy="true"/>
              <Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2200" guestport="22"/>
            </NAT>
          </Adapter>
        </Network>
        <UART>
         ....
        </UART>
        <AudioAdapter useDefault="true" driver="WAS" enabled="true"/>
        <RTC localOrUTC="UTC"/>
        <SharedFolders>
          ...
        </SharedFolders>
        <Clipboard mode="Bidirectional"/>
        <GuestProperties>
          ....
        </GuestProperties>
        <StorageControllers>
        ....
        </StorageControllers>
      </Hardware>
    </Snapshot>
    <Hardware>
      <CPU count="4">
        <PAE enabled="true"/>
        <LongMode enabled="true"/>
        <X2APIC enabled="true"/>
        <HardwareVirtExLargePages enabled="true"/>
      </CPU>
      <Memory RAMSize="12000"/>
      <Display VRAMSize="64" accelerate3D="true"/>
      <BIOS>
      ...
      </BIOS>
      <Network>
        <Adapter slot="0" enabled="true" MACAddress="..." type="...">
          <DisabledModes>
            <BridgedInterface name="Intel(R) Wi-Fi 6 AX201 160MHz"/>
            <InternalNetwork name="intnet"/>
            <NATNetwork name="NatNetwork"/>
          </DisabledModes>
          <NAT>
            <DNS use-proxy="true"/>
            <Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2200" guestport="22"/>
          </NAT>
        </Adapter>
      </Network>
      <UART>
      ....
      </UART>
      <AudioAdapter useDefault="true" driver="WAS" enabled="true"/>
      <RTC localOrUTC="UTC"/>
      <SharedFolders>
      ...
      </SharedFolders>
      <Clipboard mode="Bidirectional"/>
      <GuestProperties>
      ...
      </GuestProperties>
      <StorageControllers>
      ....
      </StorageControllers>
    </Hardware>
  </Machine>
</VirtualBox>

```





Thanks for the extra info! https://github.com/hashicorp/vagrant/pull/13056 should resolve this issue 🤞 



Patch:
diff --git a/plugins/providers/virtualbox/driver/version_7_0.rb b/plugins/providers/virtualbox/driver/version_7_0.rb
index febf529d2f6..fcb6cbdfbc4 100644
--- a/plugins/providers/virtualbox/driver/version_7_0.rb
+++ b/plugins/providers/virtualbox/driver/version_7_0.rb
@@ -257,7 +257,7 @@ def read_forwarded_ports(uuid=nil, active_only=false)
 
           File.open(result[:path], "r") do |f|
             doc = REXML::Document.new(f)
-            networks = REXML::XPath.each(doc.root, "//Adapter")
+            networks = REXML::XPath.each(doc.root, "Machine/Hardware/Network/Adapter")
             networks.each do |net|
               REXML::XPath.each(doc.root, net.xpath + "/NAT/Forwarding") do |fwd|
                 # Result Array values:


Test patch:
diff --git a/test/unit/plugins/providers/virtualbox/driver/version_7_0_test.rb b/test/unit/plugins/providers/virtualbox/driver/version_7_0_test.rb
index df6519c3070..7fed25b4ad5 100644
--- a/test/unit/plugins/providers/virtualbox/driver/version_7_0_test.rb
+++ b/test/unit/plugins/providers/virtualbox/driver/version_7_0_test.rb
@@ -683,6 +683,25 @@
 VBOX_VMCONFIG_FILE=%(<?xml version="1.0"?>
 <VirtualBox xmlns="http://www.virtualbox.org/" version="1.19-linux">
   <Machine uuid="{623842dc-0947-4143-aa4e-7d180c5eb348}" name="vagrant-test_default_1665781960041_56631" OSType="Ubuntu_64" snapshotFolder="Snapshots">
+    <Snapshot uuid="{467622d6-f25b-4aaa-94dd-e3e949efca0f}" name="Snapshot 1" timeStamp="2023-01-12T18:28:25Z">
+      <Hardware>
+        <Network>
+          <Adapter slot="0" enabled="true" MACAddress="080027BB1475" type="82540EM">
+            <NAT localhost-reachable="true">
+              <DNS use-proxy="true"/>
+              <Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2222" guestport="22"/>
+            </NAT>
+          </Adapter>
+          <Adapter slot="1" enabled="true" MACAddress="080027DD5ADF" type="82540EM">
+            <DisabledModes>
+              <InternalNetwork name="intnet"/>
+              <NATNetwork name="NatNetwork"/>
+            </DisabledModes>
+            <HostOnlyInterface name="vboxnet0"/>
+          </Adapter>
+        </Network>
+      </Hardware>
+    </Snapshot>
     <Hardware>
       <Network>
         <Adapter slot="0" enabled="true" MACAddress="080027BB1475" type="82540EM">