diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index a767f2b1e..59d0f1bcd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.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 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 319538cda..f13d45c3f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.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 diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 705472ac5..40df841c0 100644 --- a/docs/release-notes/version-2.11.md +++ b/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 diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 2205323e7..d6c3b435f 100644 --- a/netbox/ipam/forms.py +++ b/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, diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index 107a51943..e26c51b59 100644 --- a/netbox/ipam/models/ip.py +++ b/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: diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index c63fe93e7..58e7d6299 100644 --- a/netbox/netbox/views/generic.py +++ b/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" diff --git a/requirements.txt b/requirements.txt index f9a7a28ba..27a75d118 100644 --- a/requirements.txt +++ b/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