Compare commits

...

26 Commits

Author SHA1 Message Date
Jeremy Stretch
742804ecb8 Merge pull request #6616 from netbox-community/develop
Release v2.11.7
2021-06-16 16:18:52 -04:00
jeremystretch
2bf20fa501 Release NetBox v2.11.7 2021-06-16 15:59:46 -04:00
jeremystretch
685e0ce00d Closes #6588: Add support for webp files as front/rear device type images 2021-06-16 14:01:30 -04:00
jeremystretch
6a6b0236a9 Closes #6589: Standardize breadcrumb navigation for power panels and feeds 2021-06-16 13:50:35 -04:00
jeremystretch
857c70ece9 Closes #6564: Add N connector type for pass-through ports 2021-06-16 13:43:38 -04:00
jeremystretch
e68be6f041 Add Equinix Metal as a sponsor 2021-06-15 15:22:20 -04:00
Jeremy Stretch
52edeb42b5 Merge pull request #6604 from bluikko/patch-2
custom fields documentation missing word "more"
2021-06-15 10:57:58 -04:00
bluikko
c8a8bfd84d custom fields documentation missing word "more"
The "one or object types" looks like it is missing the word "more".
2021-06-15 15:05:37 +07:00
jeremystretch
9f2c4919eb Update NetDev Slack links 2021-06-14 16:41:10 -04:00
jeremystretch
f56a470cc7 Fixes #6602: Fix deletion of devices with cables attached 2021-06-14 16:38:19 -04:00
jeremystretch
54ccc705d0 Adopt IRM terminology 2021-06-14 14:08:55 -04:00
jeremystretch
7e481960f9 Optimize MPTTColumn rendering 2021-06-14 09:19:05 -04:00
jeremystretch
809d9e4697 Fixes #6584: Fix ordering of nested inventory items 2021-06-10 14:27:42 -04:00
jeremystretch
79c06442db Changelog for #6455, 6493 2021-06-08 15:39:39 -04:00
Jeremy Stretch
6195fc0d11 Merge pull request #6552 from drmsoffall/6493-diff-legacy-changes
Show change log diff for non-atomic changes
2021-06-08 15:24:22 -04:00
Jeremy Stretch
6523334a48 Merge pull request #6545 from crafty-ua/Add_ipv4_32_and_ipv6_128_prefix_support_#6455
Add ipv4 /32 and ipv6 /128 prefix support #6455
2021-06-08 15:12:25 -04:00
jeremystretch
b3cde51590 Fixes #6562: Disable ordering of secrets by assigned object 2021-06-08 14:18:24 -04:00
jeremystretch
6ec296f2a7 Fixes #6563: Fix filtering by location for cable connection forms 2021-06-08 14:15:06 -04:00
jeremystretch
cb4392628f Fixes #6553: ProviderNetwork search should match on name 2021-06-08 14:06:17 -04:00
drmsoffall
a224e5d470 Closes #6493: show ObjectChange diff for non-atomic changes 2021-06-05 19:15:25 +00:00
jeremystretch
7444110c79 PRVB 2021-06-04 11:15:12 -04:00
Jeremy Stretch
fc0c8a160b Merge pull request #6548 from netbox-community/develop
Release v2.11.6
2021-06-04 11:13:26 -04:00
Jeremy Stretch
481cc52686 Merge branch 'master' into develop 2021-06-04 11:01:33 -04:00
jeremystretch
4273b6e4fb Release v2.11.6 2021-06-04 10:59:36 -04:00
jeremystretch
5e08b2be37 Fixes #6544: Fix migration error when upgrading with VRF(s) defined 2021-06-04 10:53:13 -04:00
Your Name
a665b79f85 #6455 - initial 2021-06-04 16:46:02 +02:00
26 changed files with 89 additions and 54 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
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.5
placeholder: v2.11.7
validations:
required: true
- type: dropdown

View File

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

View File

@@ -25,7 +25,7 @@ discussions.
### Slack
For real-time chat, you can join the **#netbox** Slack channel on [NetDev Community](https://slack.netbox.dev/).
For real-time chat, you can join the **#netbox** Slack channel on [NetDev Community](https://netdev.chat/).
Unfortunately, the Slack channel does not provide long-term retention of chat
history, so try to avoid it for any discussions would benefit from being
preserved for future reference.

View File

@@ -2,8 +2,10 @@
<img src="https://raw.githubusercontent.com/netbox-community/netbox/develop/docs/netbox_logo.svg" width="400" alt="NetBox logo" />
</div>
NetBox is an IP address management (IPAM) and data center infrastructure
management (DCIM) tool. Initially conceived by the network engineering team at
![Master branch build status](https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=master)
NetBox is an infrastructure resource modeling (IRM) tool designed to empower
network automation. Initially conceived by the network engineering team at
[DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically
to address the needs of network and infrastructure engineers. It is intended to
function as a domain-specific source of truth for network operations.
@@ -14,18 +16,15 @@ complete list of requirements, see `requirements.txt`. The code is available [on
The complete documentation for NetBox can be found at [Read the Docs](https://netbox.readthedocs.io/en/stable/). A public demo instance is available at https://demo.netbox.dev.
| | status |
|-------------|------------|
| **master** | ![Build status](https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=master) |
| **develop** | ![Build status](https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=develop) |
<div align="center">
<h4>Thank you to our sponsors!</h4>
[![DigitalOcean](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/digitalocean.png)](https://try.digitalocean.com/developer-cloud)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
[![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/)
[![Equinix Metal](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/equinix.png)](https://metal.equinix.com/)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
[![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/)
<br />
[![Stellar Technologies](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/stellar.png)](https://stellar.tech/)
</div>
@@ -33,7 +32,7 @@ The complete documentation for NetBox can be found at [Read the Docs](https://ne
### Discussion
* [GitHub Discussions](https://github.com/netbox-community/netbox/discussions) - Discussion forum hosted by GitHub; ideal for Q&A and other structured discussions
* [Slack](https://slack.netbox.dev/) - Real-time chat hosted by the NetDev Community; best for unstructured discussion or just hanging out
* [Slack](https://netdev.chat/) - Real-time chat hosted by the NetDev Community; best for unstructured discussion or just hanging out
* [Google Group](https://groups.google.com/g/netbox-discuss) - Legacy mailing list; slowly being replaced by GitHub discussions
### Installation

View File

@@ -24,7 +24,7 @@ Marking a field as required will force the user to provide a value for the field
The filter logic controls how values are matched when filtering objects by the custom field. Loose filtering (the default) matches on a partial value, whereas exact matching requires a complete match of the given string to a field's value. For example, exact filtering with the string "red" will only match the exact value "red", whereas loose filtering will match on the values "red", "red-orange", or "bored". Setting the filter logic to "disabled" disables filtering by the field entirely.
A custom field must be assigned to one or object types, or models, in NetBox. Once created, custom fields will automatically appear as part of these models in the web UI and REST API. Note that not all models support custom fields.
A custom field must be assigned to one or more object types, or models, in NetBox. Once created, custom fields will automatically appear as part of these models in the web UI and REST API. Note that not all models support custom fields.
### Custom Field Validation

View File

@@ -8,7 +8,7 @@ There are several official forums for communication among the developers and com
* [GitHub issues](https://github.com/netbox-community/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in an issue.
* [GitHub Discussions](https://github.com/netbox-community/netbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue.
* [#netbox on NetDev Community Slack](https://slack.netbox.dev/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained long.
* [#netbox on NetDev Community Slack](https://netdev.chat/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained long.
* [Google Group](https://groups.google.com/g/netbox-discuss) - Legacy mailing list; slowly being phased out in favor of GitHub discussions.
## Governance

View File

@@ -2,7 +2,7 @@
# What is NetBox?
NetBox is an open source web application designed to help manage and document computer networks. Initially conceived by the network engineering team at [DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically to address the needs of network and infrastructure engineers. It encompasses the following aspects of network management:
NetBox is an infrastructure resource modeling (IRM) application designed to empower network automation. Initially conceived by the network engineering team at [DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically to address the needs of network and infrastructure engineers. NetBox is made available as open source under the Apache 2 license. It encompasses the following aspects of network management:
* **IP address management (IPAM)** - IP networks and addresses, VRFs, and VLANs
* **Equipment racks** - Organized by group and site

View File

@@ -1,5 +1,33 @@
# NetBox v2.11
## v2.11.7 (2021-06-16)
### 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
* [#6564](https://github.com/netbox-community/netbox/issues/6564) - Add N connector type for pass-through ports
* [#6588](https://github.com/netbox-community/netbox/issues/6588) - Add support for webp files as front/rear device type images
* [#6589](https://github.com/netbox-community/netbox/issues/6589) - Standardize breadcrumb navigation for power panels and feeds
### 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
* [#6602](https://github.com/netbox-community/netbox/issues/6602) - Fix deletion of devices with cables attached
---
## 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)
**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():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
).distinct()

View File

@@ -924,6 +924,7 @@ class PortTypeChoices(ChoiceSet):
TYPE_110_PUNCH = '110-punch'
TYPE_BNC = 'bnc'
TYPE_F = 'f'
TYPE_N = 'n'
TYPE_MRJ21 = 'mrj21'
TYPE_ST = 'st'
TYPE_SC = 'sc'
@@ -954,6 +955,7 @@ class PortTypeChoices(ChoiceSet):
(TYPE_110_PUNCH, '110 Punch'),
(TYPE_BNC, 'BNC'),
(TYPE_F, 'F Connector'),
(TYPE_N, 'N Connector'),
(TYPE_MRJ21, 'MRJ21'),
),
),

View File

@@ -2,6 +2,9 @@ from django.db.models import Q
from .choices import InterfaceTypeChoices
# Exclude SVG images (unsupported by PIL)
DEVICETYPE_IMAGE_FORMATS = 'image/bmp,image/gif,image/jpeg,image/png,image/tiff,image/webp'
#
# Racks

View File

@@ -1172,12 +1172,11 @@ class DeviceTypeForm(BootstrapMixin, CustomFieldModelForm):
)
widgets = {
'subdevice_role': StaticSelect2(),
# Exclude SVG images (unsupported by PIL)
'front_image': forms.ClearableFileInput(attrs={
'accept': 'image/bmp,image/gif,image/jpeg,image/png,image/tiff'
'accept': DEVICETYPE_IMAGE_FORMATS
}),
'rear_image': forms.ClearableFileInput(attrs={
'accept': 'image/bmp,image/gif,image/jpeg,image/png,image/tiff'
'accept': DEVICETYPE_IMAGE_FORMATS
})
}
@@ -3948,6 +3947,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, CustomFieldModelForm):
required=False,
query_params={
'site_id': '$termination_b_site',
'location_id': '$termination_b_location',
'rack_id': '$termination_b_rack',
}
)

View File

@@ -146,14 +146,12 @@ def nullify_connected_endpoints(instance, **kwargs):
# Disassociate the Cable from its termination points
if instance.termination_a is not None:
logger.debug(f"Nullifying termination A for cable {instance}")
instance.termination_a.cable = None
instance.termination_a._cable_peer = None
instance.termination_a.save()
model = instance.termination_a._meta.model
model.objects.filter(pk=instance.termination_a.pk).update(_cable_peer_type=None, _cable_peer_id=None)
if instance.termination_b is not None:
logger.debug(f"Nullifying termination B for cable {instance}")
instance.termination_b.cable = None
instance.termination_b._cable_peer = None
instance.termination_b.save()
model = instance.termination_b._meta.model
model.objects.filter(pk=instance.termination_b.pk).update(_cable_peer_type=None, _cable_peer_id=None)
# Delete and retrace any dependent cable paths
for cablepath in CablePath.objects.filter(path__contains=instance):

View File

@@ -694,7 +694,7 @@ class InventoryItemTable(DeviceComponentTable):
)
cable = None # Override DeviceComponentTable
class Meta(DeviceComponentTable.Meta):
class Meta(BaseTable.Meta):
model = InventoryItem
fields = (
'pk', 'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
@@ -715,7 +715,7 @@ class DeviceInventoryItemTable(InventoryItemTable):
buttons=('edit', 'delete')
)
class Meta(DeviceComponentTable.Meta):
class Meta(BaseTable.Meta):
model = InventoryItem
fields = (
'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()
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(
instance.prechange_data or dict(),
prechange_data or dict(),
instance.postchange_data or dict(),
exclude=['last_updated'],
)
diff_removed = {
x: instance.prechange_data.get(x) for x in diff_added
} if instance.prechange_data else {}
x: prechange_data.get(x) for x in diff_added
} if prechange_data else {}
else:
diff_added = None
diff_removed = None
@@ -221,7 +228,8 @@ class ObjectChangeView(generic.ObjectView):
'next_change': next_change,
'prev_change': prev_change,
'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():
vrf_count = Prefix.objects.filter(vrf=vrf).count()
self.stdout.write(f'VRF {vrf}: {vrf_count} prefixes...')
rebuild_prefixes(vrf)
rebuild_prefixes(vrf.pk)
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
for vrf in VRF.objects.all():
rebuild_prefixes(vrf)
rebuild_prefixes(vrf.pk)
class Migration(migrations.Migration):

View File

@@ -340,16 +340,6 @@ class Prefix(PrimaryModel):
'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)
if (self.vrf is None and settings.ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
duplicate_prefixes = self.get_duplicates()
@@ -471,8 +461,8 @@ class Prefix(PrimaryModel):
child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()])
available_ips = prefix - child_ips
# IPv6, pool, or IPv4 /31 sets are fully usable
if self.family == 6 or self.is_pool or self.prefix.prefixlen == 31:
# IPv6, pool, or IPv4 /31-/32 sets are fully usable
if self.family == 6 or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31):
return available_ips
# For "normal" IPv4 prefixes, omit first and last addresses

View File

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

View File

@@ -16,7 +16,7 @@ from django.core.validators import URLValidator
# Environment setup
#
VERSION = '2.11.5'
VERSION = '2.11.7'
# Hostname
HOSTNAME = platform.node()

View File

@@ -37,6 +37,7 @@ class SecretTable(BaseTable):
)
assigned_object = tables.Column(
linkify=True,
orderable=False,
verbose_name='Assigned object'
)
role = tables.Column(

View File

@@ -7,10 +7,10 @@
{% block breadcrumbs %}
<li><a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a></li>
<li><a href="{{ object.power_panel.site.get_absolute_url }}">{{ object.power_panel.site }}</a></li>
<li><a href="{{ object.power_panel.get_absolute_url }}">{{ object.power_panel }}</a></li>
<li><a href="{% url 'dcim:powerfeed_list' %}?site_id={{ object.power_panel.site.pk }}">{{ object.power_panel.site }}</a></li>
<li><a href="{% url 'dcim:powerfeed_list' %}?power_panel_id={{ object.power_panel.pk }}">{{ object.power_panel }}</a></li>
{% if object.rack %}
<li><a href="{{ object.rack.get_absolute_url }}">{{ object.rack }}</a></li>
<li><a href="{% url 'dcim:powerfeed_list' %}?rack_id={{ object.rack.pk }}">{{ object.rack }}</a></li>
{% endif %}
<li>{{ object }}</li>
{% endblock %}

View File

@@ -5,7 +5,7 @@
{% block breadcrumbs %}
<li><a href="{% url 'dcim:powerpanel_list' %}">Power Panels</a></li>
<li><a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a></li>
<li><a href="{% url 'dcim:powerpanel_list' %}?site_id={{ object.site.pk }}">{{ object.site }}</a></li>
{% if object.location %}
<li><a href="{{ object.location.get_absolute_url }}">{{ object.location }}</a></li>
{% endif %}

View File

@@ -128,6 +128,8 @@
<span{% if k in diff_removed %} style="background-color: #ffdce0"{% endif %}>{{ k }}: {{ v|render_json }}</span>
{% endspaceless %}
{% endfor %}</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 %}
<span class="text-muted">None</span>
{% endif %}

View File

@@ -340,8 +340,11 @@ class MPTTColumn(tables.TemplateColumn):
"""
Display a nested hierarchy for MPTT-enabled models.
"""
template_code = """{% for i in record.get_ancestors %}<i class="mdi mdi-circle-small"></i>{% endfor %}""" \
"""<a href="{{ record.get_absolute_url }}">{{ record.name }}</a>"""
template_code = """
{% 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):
super().__init__(

View File

@@ -17,7 +17,7 @@ Jinja2==3.0.1
Markdown==3.3.4
netaddr==0.8.0
Pillow==8.2.0
psycopg2-binary==2.8.6
psycopg2-binary==2.9
pycryptodome==3.10.1
PyYAML==5.4.1
svgwrite==1.4.1