jeremystretch 4 лет назад
Родитель
Сommit
aed07a8ec5

+ 1 - 1
.github/ISSUE_TEMPLATE/bug_report.yaml

@@ -17,7 +17,7 @@ body:
         What version of NetBox are you currently running? (If you don't have access to the most
         recent NetBox release, consider testing on our [demo instance](https://demo.netbox.dev/)
         before opening a bug report to see if your issue has already been addressed.)
-      placeholder: v2.11.10
+      placeholder: v2.11.11
     validations:
       required: true
   - type: dropdown

+ 1 - 1
.github/ISSUE_TEMPLATE/feature_request.yaml

@@ -14,7 +14,7 @@ body:
     attributes:
       label: NetBox version
       description: What version of NetBox are you currently running?
-      placeholder: v2.11.10
+      placeholder: v2.11.11
     validations:
       required: true
   - type: dropdown

+ 3 - 1
docs/release-notes/version-2.11.md

@@ -1,6 +1,6 @@
 # NetBox v2.11
 
-## v2.11.11 (FUTURE)
+## v2.11.11 (2021-08-12)
 
 ### Enhancements
 
@@ -11,10 +11,12 @@
 
 * [#6740](https://github.com/netbox-community/netbox/issues/6740) - Add import button to VM interfaces list
 * [#6892](https://github.com/netbox-community/netbox/issues/6892) - Fix validation of unit ranges when creating a rack reservation
+* [#6896](https://github.com/netbox-community/netbox/issues/6896) - Fix validation of IP address assigned as device/VM primary via NAT relation
 * [#6902](https://github.com/netbox-community/netbox/issues/6902) - Populate device field when cloning device components
 * [#6908](https://github.com/netbox-community/netbox/issues/6908) - Allow assignment of scope to VLAN groups upon import
 * [#6909](https://github.com/netbox-community/netbox/issues/6909) - Remove extraneous `site` column from VLAN group import form
 * [#6910](https://github.com/netbox-community/netbox/issues/6910) - Fix exception on invalid CSV import column name
+* [#6918](https://github.com/netbox-community/netbox/issues/6918) - Fix return URL persistence when adding multiple objects sequentially
 * [#6935](https://github.com/netbox-community/netbox/issues/6935) - Remove extraneous columns from inventory item and device bay tables
 * [#6936](https://github.com/netbox-community/netbox/issues/6936) - Add missing `parent` column to inventory item import form
 

+ 5 - 4
netbox/ipam/forms.py

@@ -4,7 +4,8 @@ from django.utils.translation import gettext as _
 
 from dcim.models import Device, Interface, Location, Rack, Region, Site, SiteGroup
 from extras.forms import (
-    AddRemoveTagsForm, CustomFieldModelBulkEditForm, CustomFieldModelCSVForm, CustomFieldModelForm, CustomFieldModelFilterForm,
+    AddRemoveTagsForm, CustomFieldModelBulkEditForm, CustomFieldModelCSVForm, CustomFieldModelForm,
+    CustomFieldModelFilterForm,
 )
 from extras.models import Tag
 from tenancy.forms import TenancyFilterForm, TenancyForm
@@ -12,8 +13,8 @@ from tenancy.models import Tenant
 from utilities.forms import (
     add_blank_choice, BootstrapMixin, BulkEditNullBooleanSelect, ContentTypeChoiceField, CSVChoiceField,
     CSVContentTypeField, CSVModelChoiceField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
-    ExpandableIPAddressField, NumericArrayField, ReturnURLForm, SlugField, StaticSelect, StaticSelectMultiple,
-    TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
+    ExpandableIPAddressField, NumericArrayField, SlugField, StaticSelect, StaticSelectMultiple, TagFilterField,
+    BOOLEAN_WITH_BLANK_CHOICES,
 )
 from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
 from .choices import *
@@ -882,7 +883,7 @@ class IPRangeFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
 # IP addresses
 #
 
-class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModelForm):
+class IPAddressForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
     device = DynamicModelChoiceField(
         queryset=Device.objects.all(),
         required=False,

+ 9 - 12
netbox/ipam/models/ip.py

@@ -811,18 +811,15 @@ class IPAddress(PrimaryModel):
 
         # Check for primary IP assignment that doesn't match the assigned device/VM
         if self.pk:
-            device = Device.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
-            if device:
-                if getattr(self.assigned_object, 'device', None) != device:
-                    raise ValidationError({
-                        'interface': f"IP address is primary for device {device} but not assigned to it!"
-                    })
-            vm = VirtualMachine.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
-            if vm:
-                if getattr(self.assigned_object, 'virtual_machine', None) != vm:
-                    raise ValidationError({
-                        'vminterface': f"IP address is primary for virtual machine {vm} but not assigned to it!"
-                    })
+            for cls, attr in ((Device, 'device'), (VirtualMachine, 'virtual_machine')):
+                parent = cls.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first()
+                if parent and getattr(self.assigned_object, attr) != parent:
+                    # Check for a NAT relationship
+                    if not self.nat_inside or getattr(self.nat_inside.assigned_object, attr) != parent:
+                        raise ValidationError({
+                            'interface': f"IP address is primary for {cls._meta.model_name} {parent} but "
+                                         f"not assigned to it!"
+                        })
 
         # Validate IP status selection
         if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:

+ 9 - 8
netbox/netbox/views/generic.py

@@ -283,19 +283,20 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
                 messages.success(request, mark_safe(msg))
 
                 if '_addanother' in request.POST:
+                    redirect_url = request.path
+                    return_url = request.GET.get('return_url')
+                    if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()):
+                        redirect_url = f'{redirect_url}?return_url={return_url}'
 
                     # If the object has clone_fields, pre-populate a new instance of the form
                     if hasattr(obj, 'clone_fields'):
-                        url = '{}?{}'.format(request.path, prepare_cloned_fields(obj))
-                        return redirect(url)
+                        redirect_url += f"{'&' if return_url else '?'}{prepare_cloned_fields(obj)}"
 
-                    return redirect(request.get_full_path())
+                    return redirect(redirect_url)
 
-                return_url = form.cleaned_data.get('return_url')
-                if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()):
-                    return redirect(return_url)
-                else:
-                    return redirect(self.get_return_url(request, obj))
+                return_url = self.get_return_url(request, obj)
+
+                return redirect(return_url)
 
             except PermissionsViolation:
                 msg = "Object save failed due to object-level permissions violation"

+ 2 - 2
requirements.txt

@@ -1,4 +1,4 @@
-Django==3.2.5
+Django==3.2.6
 django-cors-headers==3.7.0
 django-debug-toolbar==3.2.1
 django-filter==2.4.0
@@ -18,7 +18,7 @@ gunicorn==20.1.0
 Jinja2==3.0.1
 Markdown==3.3.4
 markdown-include==0.6.0
-mkdocs-material==7.2.0
+mkdocs-material==7.2.4
 netaddr==0.8.0
 Pillow==8.3.1
 psycopg2-binary==2.9.1