Merge branch 'develop' into feature

This commit is contained in:
jeremystretch 2021-06-14 09:23:01 -04:00
commit 14f696e824
13 changed files with 55 additions and 26 deletions

View File

@ -17,7 +17,7 @@ body:
What version of NetBox are you currently running? (If you don't have access to the most 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/) 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.) before opening a bug report to see if your issue has already been addressed.)
placeholder: v2.11.5 placeholder: v2.11.6
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

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

View File

@ -1,5 +1,29 @@
# NetBox v2.11 # NetBox v2.11
## v2.11.7 (FUTURE)
### Enhancements
* [#6455](https://github.com/netbox-community/netbox/issues/6455) - Permit /32 IPv4 and /128 IPv6 prefixes
* [#6493](https://github.com/netbox-community/netbox/issues/6493) - Show change log diff for non-atomic (pre-2.11) changes
### Bug Fixes
* [#6553](https://github.com/netbox-community/netbox/issues/6553) - ProviderNetwork search should match on name
* [#6562](https://github.com/netbox-community/netbox/issues/6562) - Disable ordering of secrets by assigned object
* [#6563](https://github.com/netbox-community/netbox/issues/6563) - Fix filtering by location for cable connection forms
* [#6584](https://github.com/netbox-community/netbox/issues/6584) - Fix ordering of nested inventory items
---
## v2.11.6 (2021-06-04)
### Bug Fixes
* [#6544](https://github.com/netbox-community/netbox/issues/6544) - Fix migration error when upgrading with VRF(s) defined
---
## v2.11.5 (2021-06-04) ## v2.11.5 (2021-06-04)
**NOTE:** This release includes a database migration that calculates and annotates prefix depth. It may impose a noticeable delay on the upgrade process: Users should anticipate roughly one minute of delay per 100 thousand prefixes being updated. **NOTE:** This release includes a database migration that calculates and annotates prefix depth. It may impose a noticeable delay on the upgrade process: Users should anticipate roughly one minute of delay per 100 thousand prefixes being updated.

View File

@ -104,6 +104,7 @@ class ProviderNetworkFilterSet(PrimaryModelFilterSet):
if not value.strip(): if not value.strip():
return queryset return queryset
return queryset.filter( return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) | Q(description__icontains=value) |
Q(comments__icontains=value) Q(comments__icontains=value)
).distinct() ).distinct()

View File

@ -4031,6 +4031,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, CustomFieldModelForm):
required=False, required=False,
query_params={ query_params={
'site_id': '$termination_b_site', 'site_id': '$termination_b_site',
'location_id': '$termination_b_location',
'rack_id': '$termination_b_rack', 'rack_id': '$termination_b_rack',
} }
) )

View File

@ -709,7 +709,7 @@ class InventoryItemTable(DeviceComponentTable):
) )
cable = None # Override DeviceComponentTable cable = None # Override DeviceComponentTable
class Meta(DeviceComponentTable.Meta): class Meta(BaseTable.Meta):
model = InventoryItem model = InventoryItem
fields = ( fields = (
'pk', 'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'pk', 'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
@ -730,7 +730,7 @@ class DeviceInventoryItemTable(InventoryItemTable):
buttons=('edit', 'delete') buttons=('edit', 'delete')
) )
class Meta(DeviceComponentTable.Meta): class Meta(BaseTable.Meta):
model = InventoryItem model = InventoryItem
fields = ( fields = (
'pk', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered', 'pk', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered',

View File

@ -202,15 +202,22 @@ class ObjectChangeView(generic.ObjectView):
next_change = objectchanges.filter(time__gt=instance.time).order_by('time').first() next_change = objectchanges.filter(time__gt=instance.time).order_by('time').first()
prev_change = objectchanges.filter(time__lt=instance.time).order_by('-time').first() prev_change = objectchanges.filter(time__lt=instance.time).order_by('-time').first()
if instance.prechange_data and instance.postchange_data: if not instance.prechange_data and instance.action in ['update', 'delete'] and prev_change:
non_atomic_change = True
prechange_data = prev_change.postchange_data
else:
non_atomic_change = False
prechange_data = instance.prechange_data
if prechange_data and instance.postchange_data:
diff_added = shallow_compare_dict( diff_added = shallow_compare_dict(
instance.prechange_data or dict(), prechange_data or dict(),
instance.postchange_data or dict(), instance.postchange_data or dict(),
exclude=['last_updated'], exclude=['last_updated'],
) )
diff_removed = { diff_removed = {
x: instance.prechange_data.get(x) for x in diff_added x: prechange_data.get(x) for x in diff_added
} if instance.prechange_data else {} } if prechange_data else {}
else: else:
diff_added = None diff_added = None
diff_removed = None diff_removed = None
@ -221,7 +228,8 @@ class ObjectChangeView(generic.ObjectView):
'next_change': next_change, 'next_change': next_change,
'prev_change': prev_change, 'prev_change': prev_change,
'related_changes_table': related_changes_table, 'related_changes_table': related_changes_table,
'related_changes_count': related_changes.count() 'related_changes_count': related_changes.count(),
'non_atomic_change': non_atomic_change
} }

View File

@ -22,6 +22,6 @@ class Command(BaseCommand):
for vrf in VRF.objects.all(): for vrf in VRF.objects.all():
vrf_count = Prefix.objects.filter(vrf=vrf).count() vrf_count = Prefix.objects.filter(vrf=vrf).count()
self.stdout.write(f'VRF {vrf}: {vrf_count} prefixes...') self.stdout.write(f'VRF {vrf}: {vrf_count} prefixes...')
rebuild_prefixes(vrf) rebuild_prefixes(vrf.pk)
self.stdout.write(self.style.SUCCESS('Finished.')) self.stdout.write(self.style.SUCCESS('Finished.'))

View File

@ -20,7 +20,7 @@ def populate_prefix_hierarchy(apps, schema_editor):
# Iterate through all VRFs, rebuilding each # Iterate through all VRFs, rebuilding each
for vrf in VRF.objects.all(): for vrf in VRF.objects.all():
rebuild_prefixes(vrf) rebuild_prefixes(vrf.pk)
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -311,16 +311,6 @@ class Prefix(PrimaryModel):
'prefix': "Cannot create prefix with /0 mask." 'prefix': "Cannot create prefix with /0 mask."
}) })
# Disallow host masks
if self.prefix.version == 4 and self.prefix.prefixlen == 32:
raise ValidationError({
'prefix': "Cannot create host addresses (/32) as prefixes. Create an IPv4 address instead."
})
elif self.prefix.version == 6 and self.prefix.prefixlen == 128:
raise ValidationError({
'prefix': "Cannot create host addresses (/128) as prefixes. Create an IPv6 address instead."
})
# Enforce unique IP space (if applicable) # Enforce unique IP space (if applicable)
if (self.vrf is None and settings.ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique): if (self.vrf is None and settings.ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
duplicate_prefixes = self.get_duplicates() duplicate_prefixes = self.get_duplicates()
@ -431,8 +421,8 @@ class Prefix(PrimaryModel):
child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]) child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()])
available_ips = prefix - child_ips available_ips = prefix - child_ips
# IPv6, pool, or IPv4 /31 sets are fully usable # IPv6, pool, or IPv4 /31-/32 sets are fully usable
if self.family == 6 or self.is_pool or self.prefix.prefixlen == 31: if self.family == 6 or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31):
return available_ips return available_ips
# For "normal" IPv4 prefixes, omit first and last addresses # For "normal" IPv4 prefixes, omit first and last addresses

View File

@ -522,7 +522,7 @@ class IPAddressView(generic.ObjectView):
# Parent prefixes table # Parent prefixes table
parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
vrf=instance.vrf, vrf=instance.vrf,
prefix__net_contains=str(instance.address.ip) prefix__net_contains_or_equals=str(instance.address.ip)
).prefetch_related( ).prefetch_related(
'site', 'role' 'site', 'role'
) )

View File

@ -129,6 +129,8 @@
<span{% if k in diff_removed %} class="removed"{% endif %}>{{ k }}: {{ v|render_json }}</span> <span{% if k in diff_removed %} class="removed"{% endif %}>{{ k }}: {{ v|render_json }}</span>
{% endspaceless %}{% endfor %} {% endspaceless %}{% endfor %}
</pre> </pre>
{% elif non_atomic_change %}
Warning: Comparing non-atomic change to previous change record (<a href="{% url 'extras:objectchange' pk=prev_change.pk %}">{{ prev_change.pk }}</a>)
{% else %} {% else %}
<span class="text-muted">None</span> <span class="text-muted">None</span>
{% endif %} {% endif %}

View File

@ -349,8 +349,11 @@ class MPTTColumn(tables.TemplateColumn):
""" """
Display a nested hierarchy for MPTT-enabled models. Display a nested hierarchy for MPTT-enabled models.
""" """
template_code = """{% for i in record.get_ancestors %}<i class="mdi mdi-circle-small"></i>{% endfor %}""" \ template_code = """
"""<a href="{{ record.get_absolute_url }}">{{ record.name }}</a>""" {% load helpers %}
{% for i in record.level|as_range %}<i class="mdi mdi-circle-small"></i>{% endfor %}
<a href="{{ record.get_absolute_url }}">{{ record.name }}</a>
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__( super().__init__(