Merge branch 'develop' into feature

This commit is contained in:
jeremystretch 2022-05-12 14:16:01 -04:00
commit e208404e3a
14 changed files with 185 additions and 12 deletions

View File

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

View File

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

View File

@ -60,6 +60,8 @@ The complete documentation for NetBox can be found at [docs.netbox.dev](https://
                     
[![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/) [![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/)
<br /> <br />
[![Sentry](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/sentry.png)](https://sentry.io/)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
[![Stellar Technologies](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/stellar.png)](https://stellar.tech/) [![Stellar Technologies](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/stellar.png)](https://stellar.tech/)
</div> </div>

View File

@ -102,6 +102,10 @@ psycopg2-binary
# https://github.com/yaml/pyyaml # https://github.com/yaml/pyyaml
PyYAML PyYAML
# Sentry SDK
# https://github.com/getsentry/sentry-python
sentry-sdk
# Social authentication framework # Social authentication framework
# https://github.com/python-social-auth/social-core # https://github.com/python-social-auth/social-core
social-auth-core social-auth-core

View File

@ -0,0 +1,46 @@
# Error Reporting
## Sentry
### Enabling Error Reporting
NetBox v3.2.3 and later support native integration with [Sentry](https://sentry.io/) for automatic error reporting. To enable this functionality, simply set `SENTRY_ENABLED` to True in `configuration.py`. Errors will be sent to a Sentry ingestor maintained by the NetBox team for analysis.
```python
SENTRY_ENABLED = True
```
### Using a Custom DSN
If you prefer instead to use your own Sentry ingestor, you'll need to first create a new project under your Sentry account to represent your NetBox deployment and obtain its corresponding data source name (DSN). This looks like a URL similar to the example below:
```
https://examplePublicKey@o0.ingest.sentry.io/0
```
Once you have obtained a DSN, configure Sentry in NetBox's `configuration.py` file with the following parameters:
```python
SENTRY_ENABLED = True
SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
```
### Assigning Tags
You can optionally attach one or more arbitrary tags to the outgoing error reports if desired by setting the `SENTRY_TAGS` parameter:
```python
SENTRY_TAGS = {
"custom.foo": "123",
"custom.bar": "abc",
}
```
!!! warning "Reserved tag prefixes"
Avoid using any tag names which begin with `netbox.`, as this prefix is reserved by the NetBox application.
### Testing
Once the configuration has been saved, restart the NetBox service.
To test Sentry operation, try generating a 404 (page not found) error by navigating to an invalid URL, such as `https://netbox/404-error-testing`. (Be sure that debug mode has been disabled.) After receiving a 404 response from the NetBox server, you should see the issue appear shortly in Sentry.

View File

@ -0,0 +1,54 @@
# Error Reporting Settings
## SENTRY_DSN
Default: None
Defines a Sentry data source name (DSN) for automated error reporting. `SENTRY_ENABLED` must be True for this parameter to take effect. For example:
```
SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
```
---
## SENTRY_ENABLED
Default: False
Set to True to enable automatic error reporting via [Sentry](https://sentry.io/).
---
## SENTRY_SAMPLE_RATE
Default: 1.0 (all)
The sampling rate for errors. Must be a value between 0 (disabled) and 1.0 (report on all errors).
---
## SENTRY_TAGS
An optional dictionary of tag names and values to apply to Sentry error reports.For example:
```
SENTRY_TAGS = {
"custom.foo": "123",
"custom.bar": "abc",
}
```
!!! warning "Reserved tag prefixes"
Avoid using any tag names which begin with `netbox.`, as this prefix is reserved by the NetBox application.
---
## SENTRY_TRACES_SAMPLE_RATE
Default: 0 (disabled)
The sampling rate for transactions. Must be a value between 0 (disabled) and 1.0 (report on all transactions).
!!! warning "Consider performance implications"
A high sampling rate for transactions can induce significant performance penalties. If transaction reporting is desired, it is recommended to use a relatively low sample rate of 10% to 20% (0.1 to 0.2).

View File

@ -1,21 +1,30 @@
# NetBox v3.2 # NetBox v3.2
## v3.2.3 (FUTURE) ## v3.2.4 (FUTURE)
---
## v3.2.3 (2022-05-12)
### Enhancements ### Enhancements
* [#8805](https://github.com/netbox-community/netbox/issues/8805) - Add "mixed" option for device airflow indication
* [#8894](https://github.com/netbox-community/netbox/issues/8894) - Include full names when listing users * [#8894](https://github.com/netbox-community/netbox/issues/8894) - Include full names when listing users
* [#8998](https://github.com/netbox-community/netbox/issues/8998) - Enable filtering racks & reservations by site group * [#8998](https://github.com/netbox-community/netbox/issues/8998) - Enable filtering racks & reservations by site group
* [#9122](https://github.com/netbox-community/netbox/issues/9122) - Introduce `clearcache` management command & clear cache during upgrade * [#9122](https://github.com/netbox-community/netbox/issues/9122) - Introduce `clearcache` management command & clear cache during upgrade
* [#9221](https://github.com/netbox-community/netbox/issues/9221) - Add definition list support for Markdown
* [#9260](https://github.com/netbox-community/netbox/issues/9260) - Apply user preferences to tables under object detail views * [#9260](https://github.com/netbox-community/netbox/issues/9260) - Apply user preferences to tables under object detail views
* [#9278](https://github.com/netbox-community/netbox/issues/9278) - Linkify device types count under manufacturers list * [#9278](https://github.com/netbox-community/netbox/issues/9278) - Linkify device types count under manufacturers list
* [#9280](https://github.com/netbox-community/netbox/issues/9280) - Allow adopting existing components when installing a module * [#9280](https://github.com/netbox-community/netbox/issues/9280) - Allow adopting existing components when installing a module
* [#9314](https://github.com/netbox-community/netbox/issues/9314) - Add device and VM filters for FHRP group assignments * [#9314](https://github.com/netbox-community/netbox/issues/9314) - Add device and VM filters for FHRP group assignments
* [#9340](https://github.com/netbox-community/netbox/issues/9340) - Introduce support for error reporting via Sentry
* [#9343](https://github.com/netbox-community/netbox/issues/9343) - Add Ubiquiti SmartPower power outlet type
### Bug Fixes ### Bug Fixes
* [#9190](https://github.com/netbox-community/netbox/issues/9190) - Prevent exception when attempting to instantiate module components which already exist on the parent device * [#9190](https://github.com/netbox-community/netbox/issues/9190) - Prevent exception when attempting to instantiate module components which already exist on the parent device
* [#9267](https://github.com/netbox-community/netbox/issues/9267) - Remove invalid entry in IP address role choices * [#9267](https://github.com/netbox-community/netbox/issues/9267) - Remove invalid entry in IP address role choices
* [#9296](https://github.com/netbox-community/netbox/issues/9296) - Improve Markdown link sanitization
* [#9306](https://github.com/netbox-community/netbox/issues/9306) - Include VC master interfaces when selecting a LAG/bridge for a VC member interface * [#9306](https://github.com/netbox-community/netbox/issues/9306) - Include VC master interfaces when selecting a LAG/bridge for a VC member interface
* [#9311](https://github.com/netbox-community/netbox/issues/9311) - Permit creating contact assignment without a priority via the REST API * [#9311](https://github.com/netbox-community/netbox/issues/9311) - Permit creating contact assignment without a priority via the REST API
* [#9313](https://github.com/netbox-community/netbox/issues/9313) - Remove HTML code from CSV output of many-to-many relationships * [#9313](https://github.com/netbox-community/netbox/issues/9313) - Remove HTML code from CSV output of many-to-many relationships

View File

@ -73,6 +73,7 @@ nav:
- Required Settings: 'configuration/required-settings.md' - Required Settings: 'configuration/required-settings.md'
- Optional Settings: 'configuration/optional-settings.md' - Optional Settings: 'configuration/optional-settings.md'
- Dynamic Settings: 'configuration/dynamic-settings.md' - Dynamic Settings: 'configuration/dynamic-settings.md'
- Error Reporting: 'configuration/error-reporting.md'
- Remote Authentication: 'configuration/remote-authentication.md' - Remote Authentication: 'configuration/remote-authentication.md'
- Core Functionality: - Core Functionality:
- IP Address Management: 'core-functionality/ipam.md' - IP Address Management: 'core-functionality/ipam.md'
@ -123,6 +124,7 @@ nav:
- Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md' - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md'
- Okta: 'administration/authentication/okta.md' - Okta: 'administration/authentication/okta.md'
- Permissions: 'administration/permissions.md' - Permissions: 'administration/permissions.md'
- Error Reporting: 'administration/error-reporting.md'
- Housekeeping: 'administration/housekeeping.md' - Housekeeping: 'administration/housekeeping.md'
- Replicating NetBox: 'administration/replicating-netbox.md' - Replicating NetBox: 'administration/replicating-netbox.md'
- NetBox Shell: 'administration/netbox-shell.md' - NetBox Shell: 'administration/netbox-shell.md'

View File

@ -159,6 +159,7 @@ class DeviceAirflowChoices(ChoiceSet):
AIRFLOW_RIGHT_TO_LEFT = 'right-to-left' AIRFLOW_RIGHT_TO_LEFT = 'right-to-left'
AIRFLOW_SIDE_TO_REAR = 'side-to-rear' AIRFLOW_SIDE_TO_REAR = 'side-to-rear'
AIRFLOW_PASSIVE = 'passive' AIRFLOW_PASSIVE = 'passive'
AIRFLOW_MIXED = 'mixed'
CHOICES = ( CHOICES = (
(AIRFLOW_FRONT_TO_REAR, 'Front to rear'), (AIRFLOW_FRONT_TO_REAR, 'Front to rear'),
@ -167,6 +168,7 @@ class DeviceAirflowChoices(ChoiceSet):
(AIRFLOW_RIGHT_TO_LEFT, 'Right to left'), (AIRFLOW_RIGHT_TO_LEFT, 'Right to left'),
(AIRFLOW_SIDE_TO_REAR, 'Side to rear'), (AIRFLOW_SIDE_TO_REAR, 'Side to rear'),
(AIRFLOW_PASSIVE, 'Passive'), (AIRFLOW_PASSIVE, 'Passive'),
(AIRFLOW_MIXED, 'Mixed'),
) )
@ -575,6 +577,7 @@ class PowerOutletTypeChoices(ChoiceSet):
TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32a' TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32a'
TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1' TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1'
TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top' TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top'
TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower'
# Other # Other
TYPE_HARDWIRED = 'hardwired' TYPE_HARDWIRED = 'hardwired'
@ -683,6 +686,7 @@ class PowerOutletTypeChoices(ChoiceSet):
(TYPE_NEUTRIK_POWERCON_32A, 'Neutrik powerCON (32A)'), (TYPE_NEUTRIK_POWERCON_32A, 'Neutrik powerCON (32A)'),
(TYPE_NEUTRIK_POWERCON_TRUE1, 'Neutrik powerCON TRUE1'), (TYPE_NEUTRIK_POWERCON_TRUE1, 'Neutrik powerCON TRUE1'),
(TYPE_NEUTRIK_POWERCON_TRUE1_TOP, 'Neutrik powerCON TRUE1 TOP'), (TYPE_NEUTRIK_POWERCON_TRUE1_TOP, 'Neutrik powerCON TRUE1 TOP'),
(TYPE_UBIQUITI_SMARTPOWER, 'Ubiquiti SmartPower'),
)), )),
('Other', ( ('Other', (
(TYPE_HARDWIRED, 'Hardwired'), (TYPE_HARDWIRED, 'Hardwired'),

View File

@ -1,3 +1,4 @@
import hashlib
import importlib import importlib
import logging import logging
import os import os
@ -8,9 +9,11 @@ import sys
import warnings import warnings
from urllib.parse import urlsplit from urllib.parse import urlsplit
import sentry_sdk
from django.contrib.messages import constants as messages from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.validators import URLValidator from django.core.validators import URLValidator
from sentry_sdk.integrations.django import DjangoIntegration
from netbox.config import PARAMS from netbox.config import PARAMS
@ -40,6 +43,7 @@ if sys.version_info < (3, 8):
f"NetBox requires Python 3.8 or later. (Currently installed: Python {platform.python_version()})" f"NetBox requires Python 3.8 or later. (Currently installed: Python {platform.python_version()})"
) )
DEFAULT_SENTRY_DSN = 'https://198cf560b29d4054ab8e583a1d10ea58@o1242133.ingest.sentry.io/6396485'
# #
# Configuration import # Configuration import
@ -68,6 +72,9 @@ DATABASE = getattr(configuration, 'DATABASE')
REDIS = getattr(configuration, 'REDIS') REDIS = getattr(configuration, 'REDIS')
SECRET_KEY = getattr(configuration, 'SECRET_KEY') SECRET_KEY = getattr(configuration, 'SECRET_KEY')
# Calculate a unique deployment ID from the secret key
DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16]
# Set static config parameters # Set static config parameters
ADMINS = getattr(configuration, 'ADMINS', []) ADMINS = getattr(configuration, 'ADMINS', [])
AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', []) AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [])
@ -113,6 +120,11 @@ REMOTE_AUTH_GROUP_SEPARATOR = getattr(configuration, 'REMOTE_AUTH_GROUP_SEPARATO
REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/') REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/')
RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300) RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/') SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/')
SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', DEFAULT_SENTRY_DSN)
SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False)
SENTRY_SAMPLE_RATE = getattr(configuration, 'SENTRY_SAMPLE_RATE', 1.0)
SENTRY_TRACES_SAMPLE_RATE = getattr(configuration, 'SENTRY_TRACES_SAMPLE_RATE', 0)
SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {})
SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None) SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid') SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d') SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
@ -428,6 +440,36 @@ EXEMPT_PATHS = (
) )
#
# Sentry
#
if SENTRY_ENABLED:
if not SENTRY_DSN:
raise ImproperlyConfigured("SENTRY_ENABLED is True but SENTRY_DSN has not been defined.")
# If using the default DSN, force sampling rates
if SENTRY_DSN == DEFAULT_SENTRY_DSN:
SENTRY_SAMPLE_RATE = 1.0
SENTRY_TRACES_SAMPLE_RATE = 0
# Initialize the SDK
sentry_sdk.init(
dsn=SENTRY_DSN,
release=VERSION,
integrations=[DjangoIntegration()],
sample_rate=SENTRY_SAMPLE_RATE,
traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE,
send_default_pii=True,
http_proxy=HTTP_PROXIES.get('http') if HTTP_PROXIES else None,
https_proxy=HTTP_PROXIES.get('https') if HTTP_PROXIES else None
)
# Assign any configured tags
for k, v in SENTRY_TAGS.items():
sentry_sdk.set_tag(k, v)
# If using the default DSN, append a unique deployment ID tag for error correlation
if SENTRY_DSN == DEFAULT_SENTRY_DSN:
sentry_sdk.set_tag('netbox.deployment_id', DEPLOYMENT_ID)
# #
# Django social auth # Django social auth
# #

View File

@ -100,4 +100,5 @@ urlpatterns = [
path('{}'.format(settings.BASE_PATH), include(_patterns)) path('{}'.format(settings.BASE_PATH), include(_patterns))
] ]
handler404 = 'netbox.views.handler_404'
handler500 = 'netbox.views.server_error' handler500 = 'netbox.views.server_error'

View File

@ -2,7 +2,6 @@ import platform
import sys import sys
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache from django.core.cache import cache
from django.db.models import F from django.db.models import F
from django.http import HttpResponseServerError from django.http import HttpResponseServerError
@ -11,9 +10,10 @@ from django.template import loader
from django.template.exceptions import TemplateDoesNotExist from django.template.exceptions import TemplateDoesNotExist
from django.urls import reverse from django.urls import reverse
from django.views.decorators.csrf import requires_csrf_token from django.views.decorators.csrf import requires_csrf_token
from django.views.defaults import ERROR_500_TEMPLATE_NAME from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found
from django.views.generic import View from django.views.generic import View
from packaging import version from packaging import version
from sentry_sdk import capture_message
from circuits.models import Circuit, Provider from circuits.models import Circuit, Provider
from dcim.models import ( from dcim.models import (
@ -190,13 +190,21 @@ class StaticMediaFailureView(View):
""" """
Display a user-friendly error message with troubleshooting tips when a static media file fails to load. Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
""" """
def get(self, request): def get(self, request):
return render(request, 'media_failure.html', { return render(request, 'media_failure.html', {
'filename': request.GET.get('filename') 'filename': request.GET.get('filename')
}) })
def handler_404(request, exception):
"""
Wrap Django's default 404 handler to enable Sentry reporting.
"""
capture_message("Page not found", level="error")
return page_not_found(request, exception)
@requires_csrf_token @requires_csrf_token
def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
""" """

View File

@ -150,15 +150,15 @@ def render_markdown(value):
value = strip_tags(value) value = strip_tags(value)
# Sanitize Markdown links # Sanitize Markdown links
pattern = fr'\[([^\]]+)\]\((?!({schemes})).*:(.+)\)' pattern = fr'\[([^\]]+)\]\(\s*(?!({schemes})).*:(.+)\)'
value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE) value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
# Sanitize Markdown reference links # Sanitize Markdown reference links
pattern = fr'\[(.+)\]:\s*(?!({schemes}))\w*:(.+)' pattern = fr'\[([^\]]+)\]:\s*(?!({schemes}))\w*:(.+)'
value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE) value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
# Render Markdown # Render Markdown
html = markdown(value, extensions=['fenced_code', 'tables', StrikethroughExtension()]) html = markdown(value, extensions=['def_list', 'fenced_code', 'tables', StrikethroughExtension()])
# If the string is not empty wrap it in rendered-markdown to style tables # If the string is not empty wrap it in rendered-markdown to style tables
if html: if html:

View File

@ -1,5 +1,5 @@
Django==4.0.4 Django==4.0.4
django-cors-headers==3.11.0 django-cors-headers==3.12.0
django-debug-toolbar==3.2.4 django-debug-toolbar==3.2.4
django-filter==21.1 django-filter==21.1
django-graphiql-debug-toolbar==0.2.0 django-graphiql-debug-toolbar==0.2.0
@ -16,14 +16,15 @@ drf-yasg[validation]==1.20.0
graphene-django==2.15.0 graphene-django==2.15.0
gunicorn==20.1.0 gunicorn==20.1.0
Jinja2==3.1.2 Jinja2==3.1.2
Markdown==3.3.6 Markdown==3.3.7
markdown-include==0.6.0 markdown-include==0.6.0
mkdocs-material==8.2.11 mkdocs-material==8.2.14
mkdocstrings[python-legacy]==0.18.1 mkdocstrings[python-legacy]==0.18.1
netaddr==0.8.0 netaddr==0.8.0
Pillow==9.1.0 Pillow==9.1.0
psycopg2-binary==2.9.3 psycopg2-binary==2.9.3
PyYAML==6.0 PyYAML==6.0
sentry-sdk==1.5.12
social-auth-app-django==5.0.0 social-auth-app-django==5.0.0
social-auth-core==4.2.0 social-auth-core==4.2.0
svgwrite==1.4.2 svgwrite==1.4.2