Skip to content

Commit

Permalink
Merge pull request #14616 from netbox-community/develop
Browse files Browse the repository at this point in the history
Release v3.6.8
  • Loading branch information
jeremystretch authored Dec 27, 2023
2 parents f1d4011 + 07da3f6 commit 46b933a
Show file tree
Hide file tree
Showing 36 changed files with 523 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ body:
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v3.6.7
placeholder: v3.6.8
validations:
required: true
- type: dropdown
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v3.6.7
placeholder: v3.6.8
validations:
required: true
- type: dropdown
Expand Down
3 changes: 3 additions & 0 deletions docs/features/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ When entering a search query, the user can choose a specific lookup type: exact

Custom fields defined by NetBox administrators are also included in search results if configured with a search weight. Additionally, NetBox plugins can register their own custom models for inclusion alongside core models.

!!! note
NetBox does not index any static choice field's (including custom fields of type "Selection" or "Multiple selection").

## Saved Filters

Each type of object in NetBox is accompanied by an extensive set of filters, each tied to a specific attribute, which enable the creation of complex queries. Often you'll find that certain queries are used routinely to apply some set of prescribed conditions to a query. Once a set of filters has been applied, NetBox offers the option to save it for future use.
Expand Down
2 changes: 1 addition & 1 deletion docs/models/dcim/inventoryitem.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The parent inventory item to which this item is assigned (optional).

### Name

The inventory item's name. Must be unique to the parent device.
The inventory item's name. If the inventory item is assigned to a parent item, its name must be unique among its siblings (all items belonging to the same parent item).

### Label

Expand Down
26 changes: 26 additions & 0 deletions docs/release-notes/version-3.6.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# NetBox v3.6

## v3.6.8 (2023-12-27)

### Enhancements

* [#11039](https://github.com/netbox-community/netbox/issues/11039) - List parent prefixes under IP range view
* [#14507](https://github.com/netbox-community/netbox/issues/14507) - Print new NetBox version when running upgrade script
* [#14538](https://github.com/netbox-community/netbox/issues/14538) - Add the `available_at_site` filter for VLANs
* [#14596](https://github.com/netbox-community/netbox/issues/14596) - Match against description field when searching for devices

### Bug Fixes

* [#11816](https://github.com/netbox-community/netbox/issues/11816) - Correct display of error message when attempting invalid VLAN site & group assignment
* [#12731](https://github.com/netbox-community/netbox/issues/12731) - Fix custom validation for many-to-many fields
* [#13606](https://github.com/netbox-community/netbox/issues/13606) - Fix filtering custom multi-choice fields by null
* [#13649](https://github.com/netbox-community/netbox/issues/13649) - Correct calculation of absolute lengths for zero-length cables
* [#13812](https://github.com/netbox-community/netbox/issues/13812) - Update status of remote data source when syncing fails via `syncdatasource` management command
* [#13909](https://github.com/netbox-community/netbox/issues/13909) - Fix cloning of objects which have a multi-choice custom field
* [#14517](https://github.com/netbox-community/netbox/issues/14517) - Ensure reservations tab is always displayed under rack view
* [#14532](https://github.com/netbox-community/netbox/issues/14532) - Device/VM change record should accurately reflect when primary/OOB IP is deleted
* [#14549](https://github.com/netbox-community/netbox/issues/14549) - Fix association of job results when executing scripts via `runscript` management command
* [#14560](https://github.com/netbox-community/netbox/issues/14560) - Do not escape exclamation marks in custom link URLs
* [#14575](https://github.com/netbox-community/netbox/issues/14575) - Fix display of the tags column under VDC table
* [#14613](https://github.com/netbox-community/netbox/issues/14613) - Fix display of current configuration parameters in UI

---

## v3.6.7 (2023-12-15)

### Enhancements
Expand Down
11 changes: 8 additions & 3 deletions netbox/core/management/commands/syncdatasource.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.core.management.base import BaseCommand, CommandError

from core.choices import DataSourceStatusChoices
from core.models import DataSource


Expand Down Expand Up @@ -33,9 +34,13 @@ def handle(self, *args, **options):
for i, datasource in enumerate(datasources, start=1):
self.stdout.write(f"[{i}] Syncing {datasource}... ", ending='')
self.stdout.flush()
datasource.sync()
self.stdout.write(datasource.get_status_display())
self.stdout.flush()
try:
datasource.sync()
self.stdout.write(datasource.get_status_display())
self.stdout.flush()
except Exception as e:
DataSource.objects.filter(pk=datasource.pk).update(status=DataSourceStatusChoices.FAILED)
raise e

if len(options['name']) > 1:
self.stdout.write(f"Finished.")
15 changes: 9 additions & 6 deletions netbox/core/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib import messages
from django.core.cache import cache
from django.shortcuts import get_object_or_404, redirect

from extras.models import ConfigRevision
Expand Down Expand Up @@ -153,9 +154,11 @@ class ConfigView(generic.ObjectView):
queryset = ConfigRevision.objects.all()

def get_object(self, **kwargs):
if config := self.queryset.first():
return config
# Instantiate a dummy default config if none has been created yet
return ConfigRevision(
data=get_config().defaults
)
revision_id = cache.get('config_version')
try:
return ConfigRevision.objects.get(pk=revision_id)
except ConfigRevision.DoesNotExist:
# Fall back to using the active config data if no record is found
return ConfigRevision(
data=get_config()
)
1 change: 1 addition & 0 deletions netbox/dcim/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ def search(self, queryset, name, value):
Q(serial__icontains=value.strip()) |
Q(inventoryitems__serial__icontains=value.strip()) |
Q(asset_tag__icontains=value.strip()) |
Q(description_icontains=value.strip()) |
Q(comments__icontains=value) |
Q(primary_ip4__address__startswith=value) |
Q(primary_ip6__address__startswith=value)
Expand Down
22 changes: 22 additions & 0 deletions netbox/dcim/migrations/0182_zero_length_cable_fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.db import migrations


def update_cable_lengths(apps, schema_editor):
Cable = apps.get_model('dcim', 'Cable')

# Set the absolute length for any zero-length Cables
Cable.objects.filter(length=0).update(_abs_length=0)


class Migration(migrations.Migration):

dependencies = [
('dcim', '0181_rename_device_role_device_role'),
]

operations = [
migrations.RunPython(
code=update_cable_lengths,
reverse_code=migrations.RunPython.noop
),
]
2 changes: 1 addition & 1 deletion netbox/dcim/models/cables.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def save(self, *args, **kwargs):
_created = self.pk is None

# Store the given length (if any) in meters for use in database ordering
if self.length and self.length_unit:
if self.length is not None and self.length_unit:
self._abs_length = to_meters(self.length, self.length_unit)
else:
self._abs_length = None
Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/svg/cables.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def draw_cable(self, cable, terminations, cable_count=0):
if cable.type:
# Include the cable type in the tooltip
description.append(cable.get_type_display())
if cable.length and cable.length_unit:
if cable.length is not None and cable.length_unit:
# Include the cable length in the tooltip
description.append(f'{cable.length} {cable.get_length_unit_display()}')
else:
Expand All @@ -285,7 +285,7 @@ def draw_cable(self, cable, terminations, cable_count=0):
description = []
if cable.type:
labels.append(cable.get_type_display())
if cable.length and cable.length_unit:
if cable.length is not None and cable.length_unit:
# Include the cable length in the tooltip
labels.append(f'{cable.length} {cable.get_length_unit_display()}')

Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/tables/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
comments = columns.MarkdownColumn()

tags = columns.TagColumn(
url_name='dcim:vdc_list'
url_name='dcim:virtualdevicecontext_list'
)

class Meta(NetBoxTable.Meta):
Expand Down
3 changes: 1 addition & 2 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,8 +695,7 @@ class RackRackReservationsView(generic.ObjectChildrenView):
label=_('Reservations'),
badge=lambda obj: obj.reservations.count(),
permission='dcim.view_rackreservation',
weight=510,
hide_if_empty=True
weight=510
)

def get_children(self, request, parent):
Expand Down
2 changes: 1 addition & 1 deletion netbox/extras/management/commands/runscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def _run_script():
# Create the job
job = Job.objects.create(
object=module,
name=script.name,
name=script.class_name,
user=User.objects.filter(is_superuser=True).order_by('pk')[0],
job_id=uuid.uuid4()
)
Expand Down
4 changes: 1 addition & 3 deletions netbox/extras/models/customfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from django.core.validators import RegexValidator, ValidationError
from django.db import models
from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -571,8 +570,7 @@ def to_filter(self, lookup_expr=None):

# Multiselect
elif self.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
filter_class = filters.MultiValueCharFilter
kwargs['lookup_expr'] = 'has_key'
filter_class = filters.MultiValueArrayFilter

# Object
elif self.type == CustomFieldTypeChoices.TYPE_OBJECT:
Expand Down
2 changes: 1 addition & 1 deletion netbox/extras/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ def render(self, context):
text = clean_html(text, allowed_schemes)

# Sanitize link
link = urllib.parse.quote(link, safe='/:?&=%+[]@#,;')
link = urllib.parse.quote(link, safe='/:?&=%+[]@#,;!')

# Verify link scheme is allowed
result = urllib.parse.urlparse(link)
Expand Down
29 changes: 14 additions & 15 deletions netbox/extras/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,20 @@ def handle_changed_object(sender, instance, **kwargs):
else:
return

# Record an ObjectChange if applicable
if hasattr(instance, 'to_objectchange'):
if m2m_changed:
ObjectChange.objects.filter(
changed_object_type=ContentType.objects.get_for_model(instance),
changed_object_id=instance.pk,
request_id=request.id
).update(
postchange_data=instance.to_objectchange(action).postchange_data
)
else:
objectchange = instance.to_objectchange(action)
objectchange.user = request.user
objectchange.request_id = request.id
objectchange.save()
# Record an ObjectChange
if m2m_changed:
ObjectChange.objects.filter(
changed_object_type=ContentType.objects.get_for_model(instance),
changed_object_id=instance.pk,
request_id=request.id
).update(
postchange_data=instance.to_objectchange(action).postchange_data
)
else:
objectchange = instance.to_objectchange(action)
objectchange.user = request.user
objectchange.request_id = request.id
objectchange.save()

# If this is an M2M change, update the previously queued webhook (from post_save)
queue = webhooks_queue.get()
Expand Down
Loading

0 comments on commit 46b933a

Please sign in to comment.