Skip to content

Commit 870aa3a

Browse files
committed
Merge branch 'develop' into feature
2 parents b7c9ca7 + 86ada33 commit 870aa3a

File tree

12 files changed

+65
-40
lines changed

12 files changed

+65
-40
lines changed

.github/ISSUE_TEMPLATE/bug_report.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ body:
1414
attributes:
1515
label: NetBox version
1616
description: What version of NetBox are you currently running?
17-
placeholder: v3.0.10
17+
placeholder: v3.0.11
1818
validations:
1919
required: true
2020
- type: dropdown

.github/ISSUE_TEMPLATE/feature_request.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ body:
1414
attributes:
1515
label: NetBox version
1616
description: What version of NetBox are you currently running?
17-
placeholder: v3.0.10
17+
placeholder: v3.0.11
1818
validations:
1919
required: true
2020
- type: dropdown

docs/release-notes/version-3.0.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# NetBox v3.0
22

3-
## v3.0.11 (FUTURE)
3+
## v3.0.12 (FUTURE)
4+
5+
---
6+
7+
## v3.0.11 (2021-11-24)
48

59
### Enhancements
610

@@ -14,6 +18,7 @@
1418
### Bug Fixes
1519

1620
* [#7399](https://github.com/netbox-community/netbox/issues/7399) - Fix excessive CPU utilization when `AUTH_LDAP_FIND_GROUP_PERMS` is enabled
21+
* [#7657](https://github.com/netbox-community/netbox/issues/7657) - Make change logging middleware thread-safe
1722
* [#7720](https://github.com/netbox-community/netbox/issues/7720) - Fix initialization of custom script MultiObjectVar field with multiple values
1823
* [#7729](https://github.com/netbox-community/netbox/issues/7729) - Fix permissions evaluation when displaying VLAN group VLANs table
1924
* [#7739](https://github.com/netbox-community/netbox/issues/7739) - Fix exception when tracing cable across circuit with no far end termination
@@ -448,7 +453,7 @@ Note that NetBox's `rqworker` process will _not_ service custom queues by defaul
448453
* [#6154](https://github.com/netbox-community/netbox/issues/6154) - Allow decimal values for cable lengths
449454
* [#6328](https://github.com/netbox-community/netbox/issues/6328) - Build and serve documentation locally
450455

451-
### Bug Fixes (from v3.2-beta2)
456+
### Bug Fixes (from v3.0-beta2)
452457

453458
* [#6977](https://github.com/netbox-community/netbox/issues/6977) - Truncate global search dropdown on small screens
454459
* [#6979](https://github.com/netbox-community/netbox/issues/6979) - Hide "create & add another" button for circuit terminations

netbox/dcim/choices.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ class PowerPortTypeChoices(ChoiceSet):
447447
)),
448448
('International/ITA', (
449449
(TYPE_ITA_C, 'ITA Type C (CEE 7/16)'),
450-
(TYPE_ITA_E, 'ITA Type E (CEE 7/5)'),
450+
(TYPE_ITA_E, 'ITA Type E (CEE 7/6)'),
451451
(TYPE_ITA_F, 'ITA Type F (CEE 7/4)'),
452452
(TYPE_ITA_EF, 'ITA Type E/F (CEE 7/7)'),
453453
(TYPE_ITA_G, 'ITA Type G (BS 1363)'),
@@ -659,8 +659,8 @@ class PowerOutletTypeChoices(ChoiceSet):
659659
(TYPE_CS8464C, 'CS8464C'),
660660
)),
661661
('ITA/International', (
662-
(TYPE_ITA_E, 'ITA Type E (CEE7/5)'),
663-
(TYPE_ITA_F, 'ITA Type F (CEE7/3)'),
662+
(TYPE_ITA_E, 'ITA Type E (CEE 7/5)'),
663+
(TYPE_ITA_F, 'ITA Type F (CEE 7/3)'),
664664
(TYPE_ITA_G, 'ITA Type G (BS 1363)'),
665665
(TYPE_ITA_H, 'ITA Type H'),
666666
(TYPE_ITA_I, 'ITA Type I'),

netbox/extras/context_managers.py

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

33
from django.db.models.signals import m2m_changed, pre_delete, post_save
44

5-
from extras.signals import clear_webhooks, _clear_webhook_queue, _handle_changed_object, _handle_deleted_object
6-
from utilities.utils import curry
5+
from extras.signals import clear_webhooks, clear_webhook_queue, handle_changed_object, handle_deleted_object
6+
from netbox import thread_locals
7+
from netbox.request_context import set_request
78
from .webhooks import flush_webhooks
89

910

@@ -15,12 +16,8 @@ def change_logging(request):
1516
1617
:param request: WSGIRequest object with a unique `id` set
1718
"""
18-
webhook_queue = []
19-
20-
# Curry signals receivers to pass the current request
21-
handle_changed_object = curry(_handle_changed_object, request, webhook_queue)
22-
handle_deleted_object = curry(_handle_deleted_object, request, webhook_queue)
23-
clear_webhook_queue = curry(_clear_webhook_queue, webhook_queue)
19+
set_request(request)
20+
thread_locals.webhook_queue = []
2421

2522
# Connect our receivers to the post_save and post_delete signals.
2623
post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object')
@@ -38,5 +35,8 @@ def change_logging(request):
3835
clear_webhooks.disconnect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
3936

4037
# Flush queued webhooks to RQ
41-
flush_webhooks(webhook_queue)
42-
del webhook_queue
38+
flush_webhooks(thread_locals.webhook_queue)
39+
del thread_locals.webhook_queue
40+
41+
# Clear the request from thread-local storage
42+
set_request(None)

netbox/extras/signals.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
from django_prometheus.models import model_deletes, model_inserts, model_updates
88

99
from extras.validators import CustomValidator
10+
from netbox import thread_locals
1011
from netbox.config import get_config
12+
from netbox.request_context import get_request
1113
from netbox.signals import post_clean
1214
from .choices import ObjectChangeActionChoices
1315
from .models import ConfigRevision, CustomField, ObjectChange
1416
from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
1517

16-
1718
#
1819
# Change logging/webhooks
1920
#
@@ -22,22 +23,23 @@
2223
clear_webhooks = Signal()
2324

2425

25-
def _handle_changed_object(request, webhook_queue, sender, instance, **kwargs):
26+
def handle_changed_object(sender, instance, **kwargs):
2627
"""
2728
Fires when an object is created or updated.
2829
"""
30+
if not hasattr(instance, 'to_objectchange'):
31+
return
32+
33+
request = get_request()
34+
m2m_changed = False
35+
2936
def is_same_object(instance, webhook_data):
3037
return (
3138
ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and
3239
instance.pk == webhook_data['object_id'] and
3340
request.id == webhook_data['request_id']
3441
)
3542

36-
if not hasattr(instance, 'to_objectchange'):
37-
return
38-
39-
m2m_changed = False
40-
4143
# Determine the type of change being made
4244
if kwargs.get('created'):
4345
action = ObjectChangeActionChoices.ACTION_CREATE
@@ -67,6 +69,7 @@ def is_same_object(instance, webhook_data):
6769
objectchange.save()
6870

6971
# If this is an M2M change, update the previously queued webhook (from post_save)
72+
webhook_queue = thread_locals.webhook_queue
7073
if m2m_changed and webhook_queue and is_same_object(instance, webhook_queue[-1]):
7174
instance.refresh_from_db() # Ensure that we're working with fresh M2M assignments
7275
webhook_queue[-1]['data'] = serialize_for_webhook(instance)
@@ -81,13 +84,15 @@ def is_same_object(instance, webhook_data):
8184
model_updates.labels(instance._meta.model_name).inc()
8285

8386

84-
def _handle_deleted_object(request, webhook_queue, sender, instance, **kwargs):
87+
def handle_deleted_object(sender, instance, **kwargs):
8588
"""
8689
Fires when an object is deleted.
8790
"""
8891
if not hasattr(instance, 'to_objectchange'):
8992
return
9093

94+
request = get_request()
95+
9196
# Record an ObjectChange if applicable
9297
if hasattr(instance, 'to_objectchange'):
9398
objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
@@ -96,19 +101,21 @@ def _handle_deleted_object(request, webhook_queue, sender, instance, **kwargs):
96101
objectchange.save()
97102

98103
# Enqueue webhooks
104+
webhook_queue = thread_locals.webhook_queue
99105
enqueue_object(webhook_queue, instance, request.user, request.id, ObjectChangeActionChoices.ACTION_DELETE)
100106

101107
# Increment metric counters
102108
model_deletes.labels(instance._meta.model_name).inc()
103109

104110

105-
def _clear_webhook_queue(webhook_queue, sender, **kwargs):
111+
def clear_webhook_queue(sender, **kwargs):
106112
"""
107113
Delete any queued webhooks (e.g. because of an aborted bulk transaction)
108114
"""
109115
logger = logging.getLogger('webhooks')
110-
logger.info(f"Clearing {len(webhook_queue)} queued webhooks ({sender})")
116+
webhook_queue = thread_locals.webhook_queue
111117

118+
logger.info(f"Clearing {len(webhook_queue)} queued webhooks ({sender})")
112119
webhook_queue.clear()
113120

114121

netbox/netbox/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import threading
2+
3+
thread_locals = threading.local()

netbox/netbox/middleware.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import logging
12
import uuid
23
from urllib import parse
3-
import logging
44

55
from django.conf import settings
6-
from django.contrib.auth.middleware import RemoteUserMiddleware as RemoteUserMiddleware_
76
from django.contrib import auth
7+
from django.contrib.auth.middleware import RemoteUserMiddleware as RemoteUserMiddleware_
88
from django.core.exceptions import ImproperlyConfigured
99
from django.db import ProgrammingError
1010
from django.http import Http404, HttpResponseRedirect

netbox/netbox/request_context.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from netbox import thread_locals
2+
3+
4+
def set_request(request):
5+
thread_locals.request = request
6+
7+
8+
def get_request():
9+
return getattr(thread_locals, 'request', None)

netbox/templates/extras/webhook.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,23 @@ <h5 class="card-header">
149149
Additional Headers
150150
</h5>
151151
<div class="card-body">
152-
<pre>{{ object.additional_headers }}</pre>
152+
{% if object.additional_headers %}
153+
<pre>{{ object.additional_headers }}</pre>
154+
{% else %}
155+
<span class="text-muted">None</span>
156+
{% endif %}
153157
</div>
154158
</div>
155159
<div class="card">
156160
<h5 class="card-header">
157161
Body Template
158162
</h5>
159163
<div class="card-body">
160-
<pre>{{ object.body_template }}</pre>
164+
{% if object.body_template %}
165+
<pre>{{ object.body_template }}</pre>
166+
{% else %}
167+
<span class="text-muted">None</span>
168+
{% endif %}
161169
</div>
162170
</div>
163171
{% plugin_right_page object %}

0 commit comments

Comments
 (0)