Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v3.5.4 #12946

Merged
merged 30 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4a88d5e
PRVB
jeremystretch Jun 2, 2023
01d9e0a
Round rack power utilization to nearest 0.1%
candlerb Jun 8, 2023
210879d
fix contact assignment table modal
abhi1693 Jun 11, 2023
82cd6c5
Fixes #12862 - Add Button for Wireless Links in Sidebar
sudheesh001 Jun 11, 2023
e785139
Merge pull request #12857 from netbox-community/fix/12850-contacts-table
jeremystretch Jun 12, 2023
43235f1
Merge pull request #12839 from candlerb/candlerb/12838
jeremystretch Jun 12, 2023
22a0ce3
broadcast error fixes for ipv6 and /31/32
ITJamie Jun 12, 2023
74c1f7a
Merge pull request #12874 from ITJamie/broadcast_exceptions
jeremystretch Jun 13, 2023
a81924a
Merge pull request #12863 from sudheesh001/fix/12862-connection-sideb…
jeremystretch Jun 13, 2023
2e95865
Changelog for #12687, #12838, #12850, #12862
jeremystretch Jun 13, 2023
96cf95d
fixes typo in register_model_view docstring #12824
abhi1693 Jun 14, 2023
928a346
change link parsing from quote_plus to quote #12822
abhi1693 Jun 14, 2023
c8cbced
fix permission #12818
abhi1693 Jun 14, 2023
d03bfe8
fix connected device api schema #12682
abhi1693 Jun 14, 2023
85e3511
Merge pull request #12897 from netbox-community/fix/12682-openapi-con…
jeremystretch Jun 14, 2023
55e31ef
Merge pull request #12896 from netbox-community/fix/12818-perm
jeremystretch Jun 14, 2023
28b939c
Merge pull request #12894 from netbox-community/fix/12822-link-encode
jeremystretch Jun 14, 2023
36e0bf0
Merge pull request #12893 from netbox-community/feat/12824-doc
jeremystretch Jun 14, 2023
c5f71c0
Fixes #12847 - Include Missing Add buttons to Views
sudheesh001 Jun 11, 2023
f7b0e48
Merge pull request #12864 from sudheesh001/fix/12847-include-adds
jeremystretch Jun 14, 2023
0e873a0
Closes #12622: Fix assigning VLAN without site to Prefix (#12784)
dhenschen Jun 14, 2023
4d686e8
Changelog for #12622, #12682, #12818, #12822, #12847
jeremystretch Jun 14, 2023
9317588
add color to ChangeActionChoices #12828
abhi1693 Jun 14, 2023
0b21625
12474 update cable terminations when moving location between sites
arthanson May 16, 2023
8aeb317
Fixes #12845: Fix pagination of related IP addresses table
jeremystretch Jun 14, 2023
7fc69f3
Fixes #12914: Clear stored ordering from user config when cleared by …
jeremystretch Jun 15, 2023
6ef333e
Fixes #12885: Permit mounting of devices in U100 (#12901)
jeremystretch Jun 15, 2023
e11991c
Fix #12865 - Include Add Nav Buttons for Report and Script Objects (#…
stuntguy3000 Jun 15, 2023
cdce500
Changelog for #12474, #12828, #12845, #12865, #12885, #12914
jeremystretch Jun 15, 2023
54622b5
Release v3.5.4
jeremystretch Jun 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.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.5.3
placeholder: v3.5.4
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.5.3
placeholder: v3.5.4
validations:
required: true
- type: dropdown
Expand Down
25 changes: 25 additions & 0 deletions docs/release-notes/version-3.5.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# NetBox v3.5

## v3.5.4 (2023-06-20)

### Enhancements

* [#12828](https://github.com/netbox-community/netbox/issues/12828) - Define colors for staged change action choices
* [#12847](https://github.com/netbox-community/netbox/issues/12847) - Include "add" button on all device & virtual machine component list views
* [#12862](https://github.com/netbox-community/netbox/issues/12862) - Add menu navigation button to add wireless links directly
* [#12865](https://github.com/netbox-community/netbox/issues/12865) - Add "add" buttons for reports & scripts to navigation menu

### Bug Fixes

* [#12474](https://github.com/netbox-community/netbox/issues/12474) - Update cable terminations when assigning a location to a new site
* [#12622](https://github.com/netbox-community/netbox/issues/12622) - Permit the assignment of non-site VLANs to prefixes assigned to a site
* [#12682](https://github.com/netbox-community/netbox/issues/12682) - Correct OpenAPI schema for connected device API endpoint
* [#12687](https://github.com/netbox-community/netbox/issues/12687) - Allow the assignment of all /31 IP addresses to interfaces
* [#12818](https://github.com/netbox-community/netbox/issues/12818) - Fix permissions evaluation when queuing a data sync job
* [#12822](https://github.com/netbox-community/netbox/issues/12822) - Fix encoding of whitespace in custom link URLs
* [#12838](https://github.com/netbox-community/netbox/issues/12838) - Correct rounding of rack power utilization values
* [#12845](https://github.com/netbox-community/netbox/issues/12845) - Fix pagination of objects for related IP addresses table
* [#12850](https://github.com/netbox-community/netbox/issues/12850) - Fix table configuration modal for the contact assignments list
* [#12885](https://github.com/netbox-community/netbox/issues/12885) - Permit mounting of devices in rack unit 100
* [#12914](https://github.com/netbox-community/netbox/issues/12914) - Clear stored ordering from user config when cleared by request

---

## v3.5.3 (2023-06-02)

### Enhancements
Expand Down
2 changes: 1 addition & 1 deletion netbox/core/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def sync(self, request, pk):
"""
Enqueue a job to synchronize the DataSource.
"""
if not request.user.has_perm('extras.sync_datasource'):
if not request.user.has_perm('core.sync_datasource'):
raise PermissionDenied("Syncing data sources requires the core.sync_datasource permission.")

datasource = get_object_or_404(DataSource, pk=pk)
Expand Down
5 changes: 4 additions & 1 deletion netbox/dcim/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,10 @@ class ConnectedDeviceViewSet(ViewSet):
def get_view_name(self):
return "Connected Device Locator"

@extend_schema(responses={200: OpenApiTypes.OBJECT})
@extend_schema(
parameters=[_device_param, _interface_param],
responses={200: serializers.DeviceSerializer}
)
def list(self, request):

peer_device_name = request.query_params.get(self._device_param.name)
Expand Down
1 change: 1 addition & 0 deletions netbox/dcim/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#

RACK_U_HEIGHT_DEFAULT = 42
RACK_U_HEIGHT_MAX = 100

RACK_ELEVATION_BORDER_WIDTH = 2
RACK_ELEVATION_DEFAULT_LEGEND_WIDTH = 30
Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/migrations/0154_half_height_rack_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='device',
name='position',
field=models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(99.5)]),
field=models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100.5)]),
),
]
2 changes: 1 addition & 1 deletion netbox/dcim/models/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ class Device(PrimaryModel, ConfigContextModel):
decimal_places=1,
blank=True,
null=True,
validators=[MinValueValidator(1), MaxValueValidator(99.5)],
validators=[MinValueValidator(1), MaxValueValidator(RACK_U_HEIGHT_MAX + 0.5)],
verbose_name='Position (U)',
help_text=_('The lowest-numbered unit occupied by the device')
)
Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/models/racks.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Rack(PrimaryModel, WeightMixin):
u_height = models.PositiveSmallIntegerField(
default=RACK_U_HEIGHT_DEFAULT,
verbose_name='Height (U)',
validators=[MinValueValidator(1), MaxValueValidator(100)],
validators=[MinValueValidator(1), MaxValueValidator(RACK_U_HEIGHT_MAX)],
help_text=_('Height in rack units')
)
desc_units = models.BooleanField(
Expand Down Expand Up @@ -466,7 +466,7 @@ def get_power_utilization(self):
powerport.get_power_draw()['allocated'] for powerport in powerports
])

return int(allocated_draw / available_power_total * 100)
return round(allocated_draw / available_power_total * 100, 1)

@cached_property
def total_weight(self):
Expand Down
1 change: 1 addition & 0 deletions netbox/dcim/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def handle_location_site_change(instance, created, **kwargs):
Rack.objects.filter(location__in=locations).update(site=instance.site)
Device.objects.filter(location__in=locations).update(site=instance.site)
PowerPanel.objects.filter(location__in=locations).update(site=instance.site)
CableTermination.objects.filter(_location__in=locations).update(_site=instance.site)


@receiver(post_save, sender=Rack)
Expand Down
10 changes: 0 additions & 10 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2193,7 +2193,6 @@ class ConsolePortListView(generic.ObjectListView):
filterset = filtersets.ConsolePortFilterSet
filterset_form = forms.ConsolePortFilterForm
table = tables.ConsolePortTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(ConsolePort)
Expand Down Expand Up @@ -2257,7 +2256,6 @@ class ConsoleServerPortListView(generic.ObjectListView):
filterset = filtersets.ConsoleServerPortFilterSet
filterset_form = forms.ConsoleServerPortFilterForm
table = tables.ConsoleServerPortTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(ConsoleServerPort)
Expand Down Expand Up @@ -2321,7 +2319,6 @@ class PowerPortListView(generic.ObjectListView):
filterset = filtersets.PowerPortFilterSet
filterset_form = forms.PowerPortFilterForm
table = tables.PowerPortTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(PowerPort)
Expand Down Expand Up @@ -2385,7 +2382,6 @@ class PowerOutletListView(generic.ObjectListView):
filterset = filtersets.PowerOutletFilterSet
filterset_form = forms.PowerOutletFilterForm
table = tables.PowerOutletTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(PowerOutlet)
Expand Down Expand Up @@ -2449,7 +2445,6 @@ class InterfaceListView(generic.ObjectListView):
filterset = filtersets.InterfaceFilterSet
filterset_form = forms.InterfaceFilterForm
table = tables.InterfaceTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(Interface)
Expand Down Expand Up @@ -2559,7 +2554,6 @@ class FrontPortListView(generic.ObjectListView):
filterset = filtersets.FrontPortFilterSet
filterset_form = forms.FrontPortFilterForm
table = tables.FrontPortTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(FrontPort)
Expand Down Expand Up @@ -2623,7 +2617,6 @@ class RearPortListView(generic.ObjectListView):
filterset = filtersets.RearPortFilterSet
filterset_form = forms.RearPortFilterForm
table = tables.RearPortTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(RearPort)
Expand Down Expand Up @@ -2687,7 +2680,6 @@ class ModuleBayListView(generic.ObjectListView):
filterset = filtersets.ModuleBayFilterSet
filterset_form = forms.ModuleBayFilterForm
table = tables.ModuleBayTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(ModuleBay)
Expand Down Expand Up @@ -2743,7 +2735,6 @@ class DeviceBayListView(generic.ObjectListView):
filterset = filtersets.DeviceBayFilterSet
filterset_form = forms.DeviceBayFilterForm
table = tables.DeviceBayTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(DeviceBay)
Expand Down Expand Up @@ -2868,7 +2859,6 @@ class InventoryItemListView(generic.ObjectListView):
filterset = filtersets.InventoryItemFilterSet
filterset_form = forms.InventoryItemFilterForm
table = tables.InventoryItemTable
actions = ('import', 'export', 'bulk_edit', 'bulk_delete')


@register_model_view(InventoryItem)
Expand Down
6 changes: 3 additions & 3 deletions netbox/extras/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class ChangeActionChoices(ChoiceSet):
ACTION_DELETE = 'delete'

CHOICES = (
(ACTION_CREATE, 'Create'),
(ACTION_UPDATE, 'Update'),
(ACTION_DELETE, 'Delete'),
(ACTION_CREATE, 'Create', 'green'),
(ACTION_UPDATE, 'Update', 'blue'),
(ACTION_DELETE, 'Delete', 'red'),
)
2 changes: 1 addition & 1 deletion netbox/extras/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def render(self, context):
text = clean_html(text, allowed_schemes)

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

# Verify link scheme is allowed
result = urllib.parse.urlparse(link)
Expand Down
3 changes: 3 additions & 0 deletions netbox/extras/models/staging.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,6 @@ def apply(self):
instance = self.model.objects.get(pk=self.object_id)
logger.info(f'Deleting {self.model._meta.verbose_name} {instance}')
instance.delete()

def get_action_color(self):
return ChangeActionChoices.colors.get(self.action)
34 changes: 25 additions & 9 deletions netbox/ipam/forms/bulk_import.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils.translation import gettext as _

from dcim.models import Device, Interface, Site
Expand Down Expand Up @@ -181,16 +182,31 @@ class Meta:
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)

if data:
if not data:
return

site = data.get('site')
vlan_group = data.get('vlan_group')

# Limit VLAN queryset by assigned site and/or group (if specified)
query = Q()

if site:
query |= Q(**{
f"site__{self.fields['site'].to_field_name}": site
})
# Don't Forget to include VLANs without a site in the filter
query |= Q(**{
f"site__{self.fields['site'].to_field_name}__isnull": True
})

if vlan_group:
query &= Q(**{
f"group__{self.fields['vlan_group'].to_field_name}": vlan_group
})

# Limit VLAN queryset by assigned site and/or group (if specified)
params = {}
if data.get('site'):
params[f"site__{self.fields['site'].to_field_name}"] = data.get('site')
if data.get('vlan_group'):
params[f"group__{self.fields['vlan_group'].to_field_name}"] = data.get('vlan_group')
if params:
self.fields['vlan'].queryset = self.fields['vlan'].queryset.filter(**params)
queryset = self.fields['vlan'].queryset.filter(query)
self.fields['vlan'].queryset = queryset


class IPRangeImportForm(NetBoxModelImportForm):
Expand Down
6 changes: 2 additions & 4 deletions netbox/ipam/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
selector=True,
label=_('VLAN'),
query_params={
'site_id': '$site',
}
)
role = DynamicModelChoiceField(
queryset=Role.objects.all(),
Expand Down Expand Up @@ -370,7 +368,7 @@ def clean(self):
raise ValidationError(msg)
if address.version == 6 and address.prefixlen not in (127, 128):
raise ValidationError(msg)
if address.ip == address.broadcast:
if address.version == 4 and address.ip == address.broadcast and address.prefixlen not in (31, 32):
msg = f"{address} is a broadcast address, which may not be assigned to an interface."
raise ValidationError(msg)

Expand Down
59 changes: 59 additions & 0 deletions netbox/ipam/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,65 @@ def test_prefix_ipaddresses(self):
url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk})
self.assertHttpStatus(self.client.get(url), 200)

@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_prefix_import(self):
"""
Custom import test for YAML-based imports (versus CSV)
"""
IMPORT_DATA = """
prefix: 10.1.1.0/24
status: active
vlan: 101
site: Site 1
"""
# Note, a site is not tied to the VLAN to verify the fix for #12622
VLAN.objects.create(vid=101, name='VLAN101')

# Add all required permissions to the test user
self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')

form_data = {
'data': IMPORT_DATA,
'format': 'yaml'
}
response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True)
self.assertHttpStatus(response, 200)

prefix = Prefix.objects.get(prefix='10.1.1.0/24')
self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
self.assertEqual(prefix.vlan.vid, 101)
self.assertEqual(prefix.site.name, "Site 1")

@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_prefix_import_with_vlan_group(self):
"""
This test covers a unique import edge case where VLAN group is specified during the import.
"""
IMPORT_DATA = """
prefix: 10.1.2.0/24
status: active
vlan: 102
site: Site 1
vlan_group: Group 1
"""
vlan_group = VLANGroup.objects.create(name='Group 1', slug='group-1', scope=Site.objects.get(name="Site 1"))
VLAN.objects.create(vid=102, name='VLAN102', group=vlan_group)

# Add all required permissions to the test user
self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')

form_data = {
'data': IMPORT_DATA,
'format': 'yaml'
}
response = self.client.post(reverse('ipam:prefix_import'), data=form_data, follow=True)
self.assertHttpStatus(response, 200)

prefix = Prefix.objects.get(prefix='10.1.2.0/24')
self.assertEqual(prefix.status, PrefixStatusChoices.STATUS_ACTIVE)
self.assertEqual(prefix.vlan.vid, 102)
self.assertEqual(prefix.site.name, "Site 1")


class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
model = IPRange
Expand Down
8 changes: 5 additions & 3 deletions netbox/netbox/navigation/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
label=_('Connections'),
items=(
get_model_item('dcim', 'cable', _('Cables'), actions=['import']),
get_model_item('wireless', 'wirelesslink', _('Wireless Links'), actions=['import']),
get_model_item('wireless', 'wirelesslink', _('Wireless Links')),
MenuItem(
link='dcim:interface_connections_list',
link_text=_('Interface Connections'),
Expand Down Expand Up @@ -301,12 +301,14 @@
MenuItem(
link='extras:report_list',
link_text=_('Reports'),
permissions=['extras.view_report']
permissions=['extras.view_report'],
buttons=get_model_buttons('extras', "reportmodule", actions=['add'])
),
MenuItem(
link='extras:script_list',
link_text=_('Scripts'),
permissions=['extras.view_script']
permissions=['extras.view_script'],
buttons=get_model_buttons('extras', "scriptmodule", actions=['add'])
),
),
),
Expand Down
2 changes: 1 addition & 1 deletion netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# Environment setup
#

VERSION = '3.5.3'
VERSION = '3.5.4'

# Hostname
HOSTNAME = platform.node()
Expand Down
Loading