Skip to content

Commit

Permalink
Merge pull request #13294 from netbox-community/develop
Browse files Browse the repository at this point in the history
Release v3.5.7
  • Loading branch information
jeremystretch committed Jul 28, 2023
2 parents ec0dbe3 + 4f984c0 commit 6c53ca8
Show file tree
Hide file tree
Showing 28 changed files with 207 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yaml
Expand Up @@ -14,7 +14,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v3.5.6
placeholder: v3.5.7
validations:
required: true
- type: dropdown
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.yaml
Expand Up @@ -14,7 +14,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v3.5.6
placeholder: v3.5.7
validations:
required: true
- type: dropdown
Expand Down
2 changes: 1 addition & 1 deletion docs/features/ipam.md
Expand Up @@ -38,7 +38,7 @@ An example hierarchy might look like this:
* 100.64.16.1/24 (address)
* 100.64.16.2/24 (address)
* 100.64.16.3/24 (address)
* 100.64.16.9/24 (prefix)
* 100.64.19.0/24 (prefix)
* 100.64.32.0/20 (prefix)
* 100.64.32.1/24 (address)
* 100.64.32.10-99/24 (range)
Expand Down
3 changes: 3 additions & 0 deletions docs/installation/1-postgresql.md
Expand Up @@ -55,6 +55,9 @@ Within the shell, enter the following commands to create the database and user (
CREATE DATABASE netbox;
CREATE USER netbox WITH PASSWORD 'J5brHrAXFLQSif0K';
ALTER DATABASE netbox OWNER TO netbox;
-- the next two commands are needed on PostgreSQL 15 and later
\connect netbox;
GRANT CREATE ON SCHEMA public TO netbox;
```

!!! danger "Use a strong password"
Expand Down
24 changes: 14 additions & 10 deletions docs/installation/upgrading.md
Expand Up @@ -48,36 +48,40 @@ Download the [latest stable release](https://github.com/netbox-community/netbox/
Download and extract the latest version:

```no-highlight
wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
sudo tar -xzf vX.Y.Z.tar.gz -C /opt
sudo ln -sfn /opt/netbox-X.Y.Z/ /opt/netbox
# Set $NEWVER to the NetBox version being installed
NEWVER=3.5.0
wget https://github.com/netbox-community/netbox/archive/v$NEWVER.tar.gz
sudo tar -xzf v$NEWVER.tar.gz -C /opt
sudo ln -sfn /opt/netbox-$NEWVER/ /opt/netbox
```

Copy `local_requirements.txt`, `configuration.py`, and `ldap_config.py` (if present) from the current installation to the new version:

```no-highlight
sudo cp /opt/netbox-X.Y.Z/local_requirements.txt /opt/netbox/
sudo cp /opt/netbox-X.Y.Z/netbox/netbox/configuration.py /opt/netbox/netbox/netbox/
sudo cp /opt/netbox-X.Y.Z/netbox/netbox/ldap_config.py /opt/netbox/netbox/netbox/
# Set $OLDVER to the NetBox version currently installed
NEWVER=3.4.9
sudo cp /opt/netbox-$OLDVER/local_requirements.txt /opt/netbox/
sudo cp /opt/netbox-$OLDVER/netbox/netbox/configuration.py /opt/netbox/netbox/netbox/
sudo cp /opt/netbox-$OLDVER/netbox/netbox/ldap_config.py /opt/netbox/netbox/netbox/
```

Be sure to replicate your uploaded media as well. (The exact action necessary will depend on where you choose to store your media, but in general moving or copying the media directory will suffice.)

```no-highlight
sudo cp -pr /opt/netbox-X.Y.Z/netbox/media/ /opt/netbox/netbox/
sudo cp -pr /opt/netbox-$OLDVER/netbox/media/ /opt/netbox/netbox/
```

Also make sure to copy or link any custom scripts and reports that you've made. Note that if these are stored outside the project root, you will not need to copy them. (Check the `SCRIPTS_ROOT` and `REPORTS_ROOT` parameters in the configuration file above if you're unsure.)

```no-highlight
sudo cp -r /opt/netbox-X.Y.Z/netbox/scripts /opt/netbox/netbox/
sudo cp -r /opt/netbox-X.Y.Z/netbox/reports /opt/netbox/netbox/
sudo cp -r /opt/netbox-$OLDVER/netbox/scripts /opt/netbox/netbox/
sudo cp -r /opt/netbox-$OLDVER/netbox/reports /opt/netbox/netbox/
```

If you followed the original installation guide to set up gunicorn, be sure to copy its configuration as well:

```no-highlight
sudo cp /opt/netbox-X.Y.Z/gunicorn.py /opt/netbox/
sudo cp /opt/netbox-$OLDVER/gunicorn.py /opt/netbox/
```

### Option B: Clone the Git Repository
Expand Down
20 changes: 20 additions & 0 deletions docs/release-notes/version-3.5.md
@@ -1,5 +1,25 @@
# NetBox v3.5

## v3.5.7 (2023-07-28)

### Enhancements

* [#11803](https://github.com/netbox-community/netbox/issues/11803) - Move non-rack devices list to a separate tab under the rack view
* [#12625](https://github.com/netbox-community/netbox/issues/12625) - Mask sensitive parameters when viewing a configured data source
* [#13009](https://github.com/netbox-community/netbox/issues/13009) - Add IEC 10609-1 and NBR 14136 power port & outlet types
* [#13097](https://github.com/netbox-community/netbox/issues/13097) - Implement a faster initial poll for report & script results
* [#13234](https://github.com/netbox-community/netbox/issues/13234) - Add 100GBASE-X-DSFP and 100GBASE-X-SFPDD interface types

### Bug Fixes

* [#13051](https://github.com/netbox-community/netbox/issues/13051) - Fix Markdown support for table cell alignment
* [#13167](https://github.com/netbox-community/netbox/issues/13167) - Fix missing script results when fetched via REST API
* [#13233](https://github.com/netbox-community/netbox/issues/13233) - Remove extraneous VLAN group field from bulk edit form for interfaces
* [#13237](https://github.com/netbox-community/netbox/issues/13237) - Permit unauthenticated access to content types REST API endpoint when `LOGIN_REQUIRED` is false
* [#13285](https://github.com/netbox-community/netbox/issues/13285) - Fix exception when importing device type missing rack unit height value

---

## v3.5.6 (2023-07-10)

### Bug Fixes
Expand Down
3 changes: 3 additions & 0 deletions netbox/core/data_backends.py
Expand Up @@ -41,6 +41,7 @@ def _wrapper(cls):

class DataBackend:
parameters = {}
sensitive_parameters = []

def __init__(self, url, **kwargs):
self.url = url
Expand Down Expand Up @@ -86,6 +87,7 @@ class GitBackend(DataBackend):
widget=forms.TextInput(attrs={'class': 'form-control'})
)
}
sensitive_parameters = ['password']

@contextmanager
def fetch(self):
Expand Down Expand Up @@ -135,6 +137,7 @@ class S3Backend(DataBackend):
widget=forms.TextInput(attrs={'class': 'form-control'})
),
}
sensitive_parameters = ['aws_secret_access_key']

REGION_REGEX = r's3\.([a-z0-9-]+)\.amazonaws\.com'

Expand Down
22 changes: 22 additions & 0 deletions netbox/dcim/choices.py
Expand Up @@ -318,6 +318,10 @@ class PowerPortTypeChoices(ChoiceSet):
TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h'
TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h'
TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h'
# IEC 60906-1
TYPE_IEC_60906_1 = 'iec-60906-1'
TYPE_NBR_14136_10A = 'nbr-14136-10a'
TYPE_NBR_14136_20A = 'nbr-14136-20a'
# NEMA non-locking
TYPE_NEMA_115P = 'nema-1-15p'
TYPE_NEMA_515P = 'nema-5-15p'
Expand Down Expand Up @@ -429,6 +433,11 @@ class PowerPortTypeChoices(ChoiceSet):
(TYPE_IEC_3PNE6H, '3P+N+E 6H'),
(TYPE_IEC_3PNE9H, '3P+N+E 9H'),
)),
('IEC 60906-1', (
(TYPE_IEC_60906_1, 'IEC 60906-1'),
(TYPE_NBR_14136_10A, '2P+T 10A (NBR 14136)'),
(TYPE_NBR_14136_20A, '2P+T 20A (NBR 14136)'),
)),
('NEMA (Non-locking)', (
(TYPE_NEMA_115P, 'NEMA 1-15P'),
(TYPE_NEMA_515P, 'NEMA 5-15P'),
Expand Down Expand Up @@ -553,6 +562,10 @@ class PowerOutletTypeChoices(ChoiceSet):
TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h'
TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h'
TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h'
# IEC 60906-1
TYPE_IEC_60906_1 = 'iec-60906-1'
TYPE_NBR_14136_10A = 'nbr-14136-10a'
TYPE_NBR_14136_20A = 'nbr-14136-20a'
# NEMA non-locking
TYPE_NEMA_115R = 'nema-1-15r'
TYPE_NEMA_515R = 'nema-5-15r'
Expand Down Expand Up @@ -657,6 +670,11 @@ class PowerOutletTypeChoices(ChoiceSet):
(TYPE_IEC_3PNE6H, '3P+N+E 6H'),
(TYPE_IEC_3PNE9H, '3P+N+E 9H'),
)),
('IEC 60906-1', (
(TYPE_IEC_60906_1, 'IEC 60906-1'),
(TYPE_NBR_14136_10A, '2P+T 10A (NBR 14136)'),
(TYPE_NBR_14136_20A, '2P+T 20A (NBR 14136)'),
)),
('NEMA (Non-locking)', (
(TYPE_NEMA_115R, 'NEMA 1-15R'),
(TYPE_NEMA_515R, 'NEMA 5-15R'),
Expand Down Expand Up @@ -809,6 +827,8 @@ class InterfaceTypeChoices(ChoiceSet):
TYPE_100GE_CFP4 = '100gbase-x-cfp4'
TYPE_100GE_CXP = '100gbase-x-cxp'
TYPE_100GE_CPAK = '100gbase-x-cpak'
TYPE_100GE_DSFP = '100gbase-x-dsfp'
TYPE_100GE_SFP_DD = '100gbase-x-sfpdd'
TYPE_100GE_QSFP28 = '100gbase-x-qsfp28'
TYPE_100GE_QSFP_DD = '100gbase-x-qsfpdd'
TYPE_200GE_CFP2 = '200gbase-x-cfp2'
Expand Down Expand Up @@ -959,6 +979,8 @@ class InterfaceTypeChoices(ChoiceSet):
(TYPE_100GE_CFP4, 'CFP4 (100GE)'),
(TYPE_100GE_CXP, 'CXP (100GE)'),
(TYPE_100GE_CPAK, 'Cisco CPAK (100GE)'),
(TYPE_100GE_DSFP, 'DSFP (100GE)'),
(TYPE_100GE_SFP_DD, 'SFP-DD (100GE)'),
(TYPE_100GE_QSFP28, 'QSFP28 (100GE)'),
(TYPE_100GE_QSFP_DD, 'QSFP-DD (100GE)'),
(TYPE_200GE_QSFP56, 'QSFP56 (200GE)'),
Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/forms/bulk_edit.py
Expand Up @@ -1259,8 +1259,8 @@ class InterfaceBulkEditForm(
)
nullable_fields = (
'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'vdcs', 'mtu', 'description',
'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vrf', 'wireless_lans'
'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan',
'tagged_vlans', 'vrf', 'wireless_lans'
)

def __init__(self, *args, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/models/devices.py
Expand Up @@ -232,7 +232,7 @@ def clean(self):
super().clean()

# U height must be divisible by 0.5
if self.u_height % decimal.Decimal(0.5):
if decimal.Decimal(self.u_height) % decimal.Decimal(0.5):
raise ValidationError({
'u_height': "U height must be in increments of 0.5 rack units."
})
Expand Down
28 changes: 20 additions & 8 deletions netbox/dcim/views.py
Expand Up @@ -681,13 +681,6 @@ def get_extra_context(self, request, instance):
(PowerFeed.objects.restrict(request.user).filter(rack=instance), 'rack_id'),
)

# Get 0U devices located within the rack
nonracked_devices = Device.objects.filter(
rack=instance,
position__isnull=True,
parent_bay__isnull=True
).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role')

peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)

if instance.location:
Expand All @@ -704,7 +697,6 @@ def get_extra_context(self, request, instance):

return {
'related_models': related_models,
'nonracked_devices': nonracked_devices,
'next_rack': next_rack,
'prev_rack': prev_rack,
'svg_extra': svg_extra,
Expand All @@ -731,6 +723,26 @@ def get_children(self, request, parent):
return parent.reservations.restrict(request.user, 'view')


@register_model_view(Rack, 'nonracked_devices', 'nonracked-devices')
class RackNonRackedView(generic.ObjectChildrenView):
queryset = Rack.objects.all()
child_model = Device
table = tables.DeviceTable
filterset = filtersets.DeviceFilterSet
template_name = 'dcim/rack/non_racked_devices.html'
tab = ViewTab(
label=_('Non-Racked Devices'),
badge=lambda obj: obj.devices.filter(rack=obj, position__isnull=True, parent_bay__isnull=True).count(),
weight=500,
permission='dcim.view_device',
)

def get_children(self, request, parent):
return parent.devices.restrict(request.user, 'view').filter(
rack=parent, position__isnull=True, parent_bay__isnull=True
)


@register_model_view(Rack, 'edit')
class RackEditView(generic.ObjectEditView):
queryset = Rack.objects.all()
Expand Down
7 changes: 3 additions & 4 deletions netbox/extras/api/views.py
Expand Up @@ -6,7 +6,6 @@
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.routers import APIRootView
Expand Down Expand Up @@ -303,7 +302,7 @@ def list(self, request):

# Attach Job objects to each script (if any)
for script in script_list:
script.result = results.get(script.name, None)
script.result = results.get(script.class_name, None)

serializer = serializers.ScriptSerializer(script_list, many=True, context={'request': request})

Expand All @@ -314,7 +313,7 @@ def retrieve(self, request, pk):
object_type = ContentType.objects.get(app_label='extras', model='scriptmodule')
script.result = Job.objects.filter(
object_type=object_type,
name=script.name,
name=script.class_name,
status__in=JobStatusChoices.TERMINAL_STATE_CHOICES
).first()
serializer = serializers.ScriptDetailSerializer(script, context={'request': request})
Expand Down Expand Up @@ -381,7 +380,7 @@ class ContentTypeViewSet(ReadOnlyModelViewSet):
"""
Read-only list of ContentTypes. Limit results to ContentTypes pertinent to NetBox objects.
"""
permission_classes = (IsAuthenticated,)
permission_classes = [IsAuthenticatedOrLoginNotRequired]
queryset = ContentType.objects.order_by('app_label', 'model')
serializer_class = serializers.ContentTypeSerializer
filterset_class = filtersets.ContentTypeFilterSet
Expand Down
2 changes: 1 addition & 1 deletion netbox/netbox/navigation/menu.py
Expand Up @@ -46,7 +46,7 @@
get_model_item('tenancy', 'contact', _('Contacts')),
get_model_item('tenancy', 'contactgroup', _('Contact Groups')),
get_model_item('tenancy', 'contactrole', _('Contact Roles')),
get_model_item('tenancy', 'contactassignment', _('Contact Assignments'), actions=[]),
get_model_item('tenancy', 'contactassignment', _('Contact Assignments'), actions=['import']),
),
),
),
Expand Down
2 changes: 1 addition & 1 deletion netbox/netbox/settings.py
Expand Up @@ -25,7 +25,7 @@
# Environment setup
#

VERSION = '3.5.6'
VERSION = '3.5.7'

# Hostname
HOSTNAME = platform.node()
Expand Down
2 changes: 1 addition & 1 deletion netbox/project-static/dist/netbox-dark.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion netbox/project-static/dist/netbox-light.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion netbox/project-static/dist/netbox-print.css

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions netbox/project-static/styles/netbox.scss
Expand Up @@ -1002,6 +1002,18 @@ div.card-overlay {
padding: 8px;
}

th[align="left"] {
text-align: left;
}

th[align="center"] {
text-align: center;
}

th[align="right"] {
text-align: right;
}

/* Markdown widget */
.markdown-widget {
.nav-link {
Expand Down
6 changes: 5 additions & 1 deletion netbox/templates/core/datasource.html
Expand Up @@ -88,7 +88,11 @@ <h5 class="card-header">Backend</h5>
{% for name, field in object.get_backend.parameters.items %}
<tr>
<th scope="row">{{ field.label }}</th>
<td>{{ object.parameters|get_key:name|placeholder }}</td>
{% if name in object.get_backend.sensitive_parameters and not perms.core.change_datasource %}
<td>********</td>
{% else %}
<td>{{ object.parameters|get_key:name|placeholder }}</td>
{% endif %}
</tr>
{% empty %}
<tr>
Expand Down
1 change: 0 additions & 1 deletion netbox/templates/dcim/rack.html
Expand Up @@ -190,7 +190,6 @@ <h4>Rear</h4>
</div>
</div>
{% include 'inc/panels/related_objects.html' %}
{% include 'dcim/inc/nonracked_devices.html' %}
{% plugin_right_page object %}
</div>
</div>
Expand Down

0 comments on commit 6c53ca8

Please sign in to comment.