Repository: netbox-community/netbox
Pull request: Fixes: #14839 - Check for tunnel termination type in data and instance in addition to intially passed data.
### Fixes: #14839 

* Check for tunnel termination type in the form data and instance in addition to intially passed data.


Problem statement:
Unable to create tunnel termination for virtual machine
### Deployment Type

Self-hosted

### NetBox Version

v3.7.1

### Python Version

3.10

### Steps to Reproduce

1. Create tunel without any terminations at first
2. In the tunnel creation form, choose the newly selected tunnel with type virtual machine and any of its interface

### Expected Behavior

A termination for the virtual machine should be created

### Observed Behavior

Error of the form is raised

```
Select a valid choice. That choice is not one of the available choices.
```

I have debugged this a bit further, the `clean` method sets the `termination` which is always picking the Device interface no matter the selection made for the VM. Furthermore, there is an issue with the selector which always remains set for device.

So clearly, any selection made for the VM does show up in the UI but is not taken forward in any requests.

Hints from issue discussion:
The selector issue can be fixed by adding the following in the init method

```
self.fields['parent'].widget.attrs['selector'] = 'virtualization.virtualmachine'
```

But I'm still unsure where the primary issue is coming from.
Another issue I have found is that the edit does not show the selected vm related options in the drop-down

Commit message: Fixes: #14839 - Check for tunnel termination type in additional instances

Patch to review:
diff --git a/netbox/vpn/forms/model_forms.py b/netbox/vpn/forms/model_forms.py
index 1a11e62b70d..4decd29cfc8 100644
--- a/netbox/vpn/forms/model_forms.py
+++ b/netbox/vpn/forms/model_forms.py
@@ -265,9 +265,17 @@ class Meta:
     def __init__(self, *args, initial=None, **kwargs):
         super().__init__(*args, initial=initial, **kwargs)
 
-        if initial and initial.get('type') == TunnelTerminationTypeChoices.TYPE_VIRTUALMACHINE:
+        # If initial or self.data is set and the type is a VIRTUALMACHINE type, swap the field querysets.
+        if (
+            initial and initial.get('type') == TunnelTerminationTypeChoices.TYPE_VIRTUALMACHINE
+        ) or (
+            self.data and self.data.get('type') == TunnelTerminationTypeChoices.TYPE_VIRTUALMACHINE
+        ) or (
+            self.instance.pk and isinstance(self.instance.termination.parent_object, VirtualMachine)
+        ):
             self.fields['parent'].label = _('Virtual Machine')
             self.fields['parent'].queryset = VirtualMachine.objects.all()
+            self.fields['parent'].widget.attrs['selector'] = 'virtualization.virtualmachine'
             self.fields['termination'].queryset = VMInterface.objects.all()
             self.fields['termination'].widget.add_query_params({
                 'virtual_machine_id': '$parent',


Code review comments:
diff_hunk: @@ -265,9 +265,17 @@ class Meta:
     def __init__(self, *args, initial=None, **kwargs):
         super().__init__(*args, initial=initial, **kwargs)
 
-        if initial and initial.get('type') == TunnelTerminationTypeChoices.TYPE_VIRTUALMACHINE:
+        # If initial or self.data is set and the type is a VIRTUALMACHINE type, swap the field querysets.
+        if (
+            initial and initial.get('type') == TunnelTerminationTypeChoices.TYPE_VIRTUALMACHINE
+        ) or (
+            self.data and self.data.get('type') == TunnelTerminationTypeChoices.TYPE_VIRTUALMACHINE
+        ) or (
+            self.instance.pk and isinstance(self.instance.termination.parent_object, VirtualMachine)
+        ):
path: netbox/vpn/forms/model_forms.py
text: @user1:
Run-on conditionals like this are not viable from a maintainability standpoint. Please have a look at how we've used the `get_field_value()` utility function in other forms to avoid this.