Skip to content

Commit 120cbb0

Browse files
Merge pull request #4165 from netbox-community/develop
Release v2.7.5
2 parents 68fbd9b + 08ce024 commit 120cbb0

File tree

101 files changed

+4246
-2127
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+4246
-2127
lines changed

.github/stale.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Configuration for Stale (https://github.com/apps/stale)
22

3+
# Pull requests are exempt from being marked as stale
4+
only: issues
5+
36
# Number of days of inactivity before an issue becomes stale
47
daysUntilStale: 14
58

docs/additional-features/custom-scripts.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,11 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a
177177

178178
All variables support the following default options:
179179

180-
* `label` - The name of the form field
181-
* `description` - A brief description of the field
182180
* `default` - The field's default value
181+
* `description` - A brief description of the field
182+
* `label` - The name of the form field
183183
* `required` - Indicates whether the field is mandatory (default: true)
184+
* `widget` - The class of form widget to use (see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/widgets/))
184185

185186
## Example
186187

docs/configuration/optional-settings.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ In order to send email, NetBox needs an email server configured. The following i
109109
* TIMEOUT - Amount of time to wait for a connection (seconds)
110110
* FROM_EMAIL - Sender address for emails sent by NetBox
111111

112+
Email is sent from NetBox only for critical events. If you would like to test the email server configuration please use the django function [send_mail()](https://docs.djangoproject.com/en/stable/topics/email/#send-mail):
113+
114+
```
115+
# python ./manage.py nbshell
116+
>>> from django.core.mail import send_mail
117+
>>> send_mail(
118+
'Test Email Subject',
119+
'Test Email Body',
120+
121+
122+
fail_silently=False
123+
)
124+
```
125+
112126
---
113127

114128
## EXEMPT_VIEW_PERMISSIONS

docs/configuration/required-settings.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ NetBox requires access to a PostgreSQL database service to store data. This serv
2121
* `PASSWORD` - PostgreSQL password
2222
* `HOST` - Name or IP address of the database server (use `localhost` if running locally)
2323
* `PORT` - TCP port of the PostgreSQL service; leave blank for default port (5432)
24-
* `CONN_MAX_AGE` - Number in seconds for Netbox to keep database connections open. 150-300 seconds is typically a good starting point ([more info](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections)).
24+
* `CONN_MAX_AGE` - Lifetime of a [persistent database connection](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections), in seconds (150-300 is recommended)
2525

2626
Example:
2727

@@ -36,6 +36,9 @@ DATABASE = {
3636
}
3737
```
3838

39+
!!! note
40+
NetBox supports all PostgreSQL database options supported by the underlying Django framework. For a complete list of available parameters, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/settings/#databases).
41+
3942
---
4043

4144
## REDIS
@@ -85,6 +88,48 @@ REDIS = {
8588
It is highly recommended to keep the webhook and cache databases separate. Using the same database number on the
8689
same Redis instance for both may result in webhook processing data being lost during cache flushing events.
8790

91+
### Using Redis Sentinel
92+
93+
If you are using [Redis Sentinel](https://redis.io/topics/sentinel) for high-availability purposes, there is minimal
94+
configuration necessary to convert NetBox to recognize it. It requires the removal of the `HOST` and `PORT` keys from
95+
above and the addition of two new keys.
96+
97+
* `SENTINELS`: List of tuples or tuple of tuples with each inner tuple containing the name or IP address
98+
of the Redis server and port for each sentinel instance to connect to
99+
* `SENTINEL_SERVICE`: Name of the master / service to connect to
100+
101+
Example:
102+
103+
```python
104+
REDIS = {
105+
'webhooks': {
106+
'SENTINELS': [('mysentinel.redis.example.com', 6379)],
107+
'SENTINEL_SERVICE': 'netbox',
108+
'PASSWORD': '',
109+
'DATABASE': 0,
110+
'DEFAULT_TIMEOUT': 300,
111+
'SSL': False,
112+
},
113+
'caching': {
114+
'SENTINELS': [
115+
('mysentinel.redis.example.com', 6379),
116+
('othersentinel.redis.example.com', 6379)
117+
],
118+
'SENTINEL_SERVICE': 'netbox',
119+
'PASSWORD': '',
120+
'DATABASE': 1,
121+
'DEFAULT_TIMEOUT': 300,
122+
'SSL': False,
123+
}
124+
}
125+
```
126+
127+
!!! note:
128+
It is possible to have only one or the other Redis configurations to use Sentinel functionality. It is possible
129+
for example to have the webhook use sentinel via `HOST`/`PORT` and for caching to use Sentinel via
130+
`SENTINELS`/`SENTINEL_SERVICE`.
131+
132+
88133
---
89134

90135
## SECRET_KEY

docs/development/style-guide.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pycodestyle --ignore=W504,E501 netbox/
3232

3333
The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and attacks.
3434

35-
If there's a strong case for introducing a new depdency, it must meet the following criteria:
35+
If there's a strong case for introducing a new dependency, it must meet the following criteria:
3636

3737
* Its complete source code must be published and freely accessible without registration.
3838
* Its license must be conducive to inclusion in an open source project.
@@ -45,10 +45,18 @@ When adding a new dependency, a short description of the package and the URL of
4545

4646
* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and open a bug so that the entire code base can be evaluated at a later point.
4747

48+
* Prioritize readability over concision. Python is a very flexible language that typically gives us several options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it.
49+
4850
* No easter eggs. While they can be fun, NetBox must be considered as a business-critical tool. The potential, however minor, for introducing a bug caused by unnecessary logic is best avoided entirely.
4951

5052
* Constants (variables which generally do not change) should be declared in `constants.py` within each app. Wildcard imports from the file are acceptable.
5153

52-
* Every model should have a docstring. Every custom method should include an expalantion of its function.
54+
* Every model should have a docstring. Every custom method should include an explanation of its function.
5355

5456
* Nested API serializers generate minimal representations of an object. These are stored separately from the primary serializers to avoid circular dependencies. Always import nested serializers from other apps directly. For example, from within the DCIM app you would write `from ipam.api.nested_serializers import NestedIPAddressSerializer`.
57+
58+
## Branding
59+
60+
* When referring to NetBox in writing, use the proper form "NetBox," with the letters N and B capitalized. The lowercase form "netbox" should be used in code, filenames, etc. But never "Netbox" or any other deviation.
61+
62+
* There is an SVG form of the NetBox logo at [docs/netbox_logo.svg](../netbox_logo.svg). It is preferred to use this logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the prescribed size.

docs/installation/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The following sections detail how to set up a new instance of NetBox:
44

55
1. [PostgreSQL database](1-postgresql.md)
66
2. [NetBox components](2-netbox.md)
7-
3. [HTTP dameon](3-http-daemon.md)
7+
3. [HTTP daemon](3-http-daemon.md)
88
4. [LDAP authentication](4-ldap.md) (optional)
99

1010
# Upgrading

docs/installation/upgrading.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Finally, restart the WSGI services to run the new code. If you followed this gui
8888

8989
```no-highlight
9090
# sudo systemctl restart netbox
91-
# sudo systemctl restart netbox-rqworker
91+
# sudo systemctl restart netbox-rq
9292
```
9393

9494
!!! note

docs/release-notes/version-2.7.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
# v2.7.5 (2020-02-13)
2+
3+
**Note:** This release includes several database schema migrations that calculate and store copies of names for certain objects to improve natural ordering performance (see [#3799](https://github.com/netbox-community/netbox/issues/3799)). These migrations may take a few minutes to run if you have a very large number of objects defined in NetBox.
4+
5+
## Enhancements
6+
7+
* [#3766](https://github.com/netbox-community/netbox/issues/3766) - Allow custom script authors to specify the form widget for each variable
8+
* [#3799](https://github.com/netbox-community/netbox/issues/3799) - Greatly improve performance when ordering device components
9+
* [#3984](https://github.com/netbox-community/netbox/issues/3984) - Add support for Redis Sentinel
10+
* [#3986](https://github.com/netbox-community/netbox/issues/3986) - Include position numbers in SVG image when rendering rack elevations
11+
* [#4093](https://github.com/netbox-community/netbox/issues/4093) - Add more status choices for virtual machines
12+
* [#4100](https://github.com/netbox-community/netbox/issues/4100) - Add device filter to component list views
13+
* [#4113](https://github.com/netbox-community/netbox/issues/4113) - Add bulk edit functionality for device type components
14+
* [#4116](https://github.com/netbox-community/netbox/issues/4116) - Enable bulk edit and delete functions for device component list views
15+
* [#4129](https://github.com/netbox-community/netbox/issues/4129) - Add buttons to delete individual device type components
16+
17+
## Bug Fixes
18+
19+
* [#3507](https://github.com/netbox-community/netbox/issues/3507) - Fix filtering IP addresses by multiple devices
20+
* [#3995](https://github.com/netbox-community/netbox/issues/3995) - Make dropdown menus in the navigation bar scrollable on small screens
21+
* [#4083](https://github.com/netbox-community/netbox/issues/4083) - Permit nullifying applicable choice fields via API requests
22+
* [#4089](https://github.com/netbox-community/netbox/issues/4089) - Selection of power outlet type during bulk update is optional
23+
* [#4090](https://github.com/netbox-community/netbox/issues/4090) - Render URL custom fields as links under object view
24+
* [#4091](https://github.com/netbox-community/netbox/issues/4091) - Fix filtering of objects by custom fields using UI search form
25+
* [#4099](https://github.com/netbox-community/netbox/issues/4099) - Linkify interfaces on global interfaces list
26+
* [#4108](https://github.com/netbox-community/netbox/issues/4108) - Avoid extraneous database queries when rendering search forms
27+
* [#4134](https://github.com/netbox-community/netbox/issues/4134) - Device power ports and outlets should inherit type from the parent device type
28+
* [#4137](https://github.com/netbox-community/netbox/issues/4137) - Disable occupied terminations when connecting a cable to a circuit
29+
* [#4138](https://github.com/netbox-community/netbox/issues/4138) - Restore device bay counts in rack elevation diagrams
30+
* [#4146](https://github.com/netbox-community/netbox/issues/4146) - Fix enforcement of secret role assignment for secret decryption
31+
* [#4150](https://github.com/netbox-community/netbox/issues/4150) - Correct YAML rendering of config contexts
32+
* [#4159](https://github.com/netbox-community/netbox/issues/4159) - Fix implementation of Redis caching configuration
33+
34+
---
35+
136
# v2.7.4 (2020-02-04)
237

338
## Enhancements

mkdocs.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ pages:
4141
- Prometheus Metrics: 'additional-features/prometheus-metrics.md'
4242
- Reports: 'additional-features/reports.md'
4343
- Tags: 'additional-features/tags.md'
44-
- Topology Maps: 'additional-features/topology-maps.md'
4544
- Webhooks: 'additional-features/webhooks.md'
4645
- Administration:
4746
- Replicating NetBox: 'administration/replicating-netbox.md'

netbox/circuits/api/urls.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ def get_view_name(self):
1515
router.APIRootView = CircuitsRootView
1616

1717
# Field choices
18-
router.register(r'_choices', views.CircuitsFieldChoicesViewSet, basename='field-choice')
18+
router.register('_choices', views.CircuitsFieldChoicesViewSet, basename='field-choice')
1919

2020
# Providers
21-
router.register(r'providers', views.ProviderViewSet)
21+
router.register('providers', views.ProviderViewSet)
2222

2323
# Circuits
24-
router.register(r'circuit-types', views.CircuitTypeViewSet)
25-
router.register(r'circuits', views.CircuitViewSet)
26-
router.register(r'circuit-terminations', views.CircuitTerminationViewSet)
24+
router.register('circuit-types', views.CircuitTypeViewSet)
25+
router.register('circuits', views.CircuitViewSet)
26+
router.register('circuit-terminations', views.CircuitTerminationViewSet)
2727

2828
app_name = 'circuits-api'
2929
urlpatterns = router.urls

netbox/circuits/forms.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from tenancy.models import Tenant
1010
from utilities.forms import (
1111
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField, DatePicker,
12-
FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField
12+
DynamicModelChoiceField, DynamicModelMultipleChoiceField, SmallTextarea, SlugField, StaticSelect2,
13+
StaticSelect2Multiple, TagFilterField,
1314
)
1415
from .choices import CircuitStatusChoices
1516
from .models import Circuit, CircuitTermination, CircuitType, Provider
@@ -107,7 +108,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
107108
required=False,
108109
label='Search'
109110
)
110-
region = FilterChoiceField(
111+
region = DynamicModelMultipleChoiceField(
111112
queryset=Region.objects.all(),
112113
to_field_name='slug',
113114
required=False,
@@ -119,9 +120,10 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
119120
}
120121
)
121122
)
122-
site = FilterChoiceField(
123+
site = DynamicModelMultipleChoiceField(
123124
queryset=Site.objects.all(),
124125
to_field_name='slug',
126+
required=False,
125127
widget=APISelectMultiple(
126128
api_url="/api/dcim/sites/",
127129
value_field="slug",
@@ -164,6 +166,18 @@ class Meta:
164166
#
165167

166168
class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
169+
provider = DynamicModelChoiceField(
170+
queryset=Provider.objects.all(),
171+
widget=APISelect(
172+
api_url="/api/circuits/providers/"
173+
)
174+
)
175+
type = DynamicModelChoiceField(
176+
queryset=CircuitType.objects.all(),
177+
widget=APISelect(
178+
api_url="/api/circuits/circuit-types/"
179+
)
180+
)
167181
comments = CommentField()
168182
tags = TagField(
169183
required=False
@@ -180,12 +194,6 @@ class Meta:
180194
'commit_rate': "Committed rate",
181195
}
182196
widgets = {
183-
'provider': APISelect(
184-
api_url="/api/circuits/providers/"
185-
),
186-
'type': APISelect(
187-
api_url="/api/circuits/circuit-types/"
188-
),
189197
'status': StaticSelect2(),
190198
'install_date': DatePicker(),
191199
}
@@ -235,14 +243,14 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
235243
queryset=Circuit.objects.all(),
236244
widget=forms.MultipleHiddenInput
237245
)
238-
type = forms.ModelChoiceField(
246+
type = DynamicModelChoiceField(
239247
queryset=CircuitType.objects.all(),
240248
required=False,
241249
widget=APISelect(
242250
api_url="/api/circuits/circuit-types/"
243251
)
244252
)
245-
provider = forms.ModelChoiceField(
253+
provider = DynamicModelChoiceField(
246254
queryset=Provider.objects.all(),
247255
required=False,
248256
widget=APISelect(
@@ -255,7 +263,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
255263
initial='',
256264
widget=StaticSelect2()
257265
)
258-
tenant = forms.ModelChoiceField(
266+
tenant = DynamicModelChoiceField(
259267
queryset=Tenant.objects.all(),
260268
required=False,
261269
widget=APISelect(
@@ -290,17 +298,19 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
290298
required=False,
291299
label='Search'
292300
)
293-
type = FilterChoiceField(
301+
type = DynamicModelMultipleChoiceField(
294302
queryset=CircuitType.objects.all(),
295303
to_field_name='slug',
304+
required=False,
296305
widget=APISelectMultiple(
297306
api_url="/api/circuits/circuit-types/",
298307
value_field="slug",
299308
)
300309
)
301-
provider = FilterChoiceField(
310+
provider = DynamicModelMultipleChoiceField(
302311
queryset=Provider.objects.all(),
303312
to_field_name='slug',
313+
required=False,
304314
widget=APISelectMultiple(
305315
api_url="/api/circuits/providers/",
306316
value_field="slug",
@@ -311,7 +321,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
311321
required=False,
312322
widget=StaticSelect2Multiple()
313323
)
314-
region = forms.ModelMultipleChoiceField(
324+
region = DynamicModelMultipleChoiceField(
315325
queryset=Region.objects.all(),
316326
to_field_name='slug',
317327
required=False,
@@ -323,9 +333,10 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
323333
}
324334
)
325335
)
326-
site = FilterChoiceField(
336+
site = DynamicModelMultipleChoiceField(
327337
queryset=Site.objects.all(),
328338
to_field_name='slug',
339+
required=False,
329340
widget=APISelectMultiple(
330341
api_url="/api/dcim/sites/",
331342
value_field="slug",

netbox/circuits/tests/test_views.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from circuits.choices import *
44
from circuits.models import Circuit, CircuitType, Provider
5-
from utilities.testing import StandardTestCases
5+
from utilities.testing import ViewTestCases
66

77

8-
class ProviderTestCase(StandardTestCases.Views):
8+
class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
99
model = Provider
1010

1111
@classmethod
@@ -46,14 +46,9 @@ def setUpTestData(cls):
4646
}
4747

4848

49-
class CircuitTypeTestCase(StandardTestCases.Views):
49+
class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
5050
model = CircuitType
5151

52-
# Disable inapplicable tests
53-
test_get_object = None
54-
test_delete_object = None
55-
test_bulk_edit_objects = None
56-
5752
@classmethod
5853
def setUpTestData(cls):
5954

@@ -77,7 +72,7 @@ def setUpTestData(cls):
7772
)
7873

7974

80-
class CircuitTestCase(StandardTestCases.Views):
75+
class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
8176
model = Circuit
8277

8378
@classmethod

0 commit comments

Comments
 (0)