Merge branch 'feature' into 10945-recurring-jobs

This commit is contained in:
jeremystretch 2022-12-08 14:23:05 -05:00
commit f4842ad8db
17 changed files with 87 additions and 18 deletions

View File

@ -141,6 +141,22 @@ When determining the primary IP address for a device, IPv6 is preferred over IPv
---
## QUEUE_MAPPINGS
Allows changing which queues are used internally for background tasks.
```python
QUEUE_MAPPINGS = {
'webhook': 'low',
'report': 'high',
'script': 'high',
}
```
If no queue is defined the queue named `default` will be used.
---
## RELEASE_CHECK_URL
Default: None (disabled)

View File

@ -225,6 +225,9 @@ Once NetBox has been configured, we're ready to proceed with the actual installa
* Builds the documentation locally (for offline use)
* Aggregate static resource files on disk
!!! warning
If you still have a Python virtual environment active from a previous installation step, disable it now by running the `deactivate` command. This will avoid errors on systems where `sudo` has been configured to preserve the user's current environment.
```no-highlight
sudo /opt/netbox/upgrade.sh
```

View File

@ -2,6 +2,18 @@
## v3.3.10 (FUTURE)
### Enhancements
* [#10748](https://github.com/netbox-community/netbox/issues/10748) - Add provider selection field for provider networks to circuit termination edit view
* [#11119](https://github.com/netbox-community/netbox/issues/11119) - Enable filtering L2VPNs by slug
### Bug Fixes
* [#11041](https://github.com/netbox-community/netbox/issues/11041) - Correct power utilization percentage precision
* [#11087](https://github.com/netbox-community/netbox/issues/11087) - Fix background color of bottom banner content
* [#11101](https://github.com/netbox-community/netbox/issues/11101) - Correct circuits count under site view
* [#11128](https://github.com/netbox-community/netbox/issues/11128) - Disable ordering changelog table by object to avoid exception
---
## v3.3.9 (2022-11-30)

View File

@ -6,6 +6,7 @@
* [#815](https://github.com/netbox-community/netbox/issues/815) - Enable specifying terminations when bulk importing circuits
* [#11090](https://github.com/netbox-community/netbox/issues/11090) - Add regular expression support to global search engine
* [#11022](https://github.com/netbox-community/netbox/issues/11022) - Introduce `QUEUE_MAPPINGS` configuration parameter to allow customization of background task prioritization
### Bug Fixes

View File

@ -145,16 +145,28 @@ class CircuitTerminationForm(NetBoxModelForm):
},
required=False
)
provider_network_provider = DynamicModelChoiceField(
queryset=Provider.objects.all(),
required=False,
label='Provider',
initial_params={
'networks': 'provider_network'
}
)
provider_network = DynamicModelChoiceField(
queryset=ProviderNetwork.objects.all(),
query_params={
'provider_id': '$provider_network_provider',
},
required=False
)
class Meta:
model = CircuitTermination
fields = [
'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network', 'mark_connected',
'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', 'description', 'tags',
'provider', 'circuit', 'term_side', 'region', 'site_group', 'site', 'provider_network_provider',
'provider_network', 'mark_connected', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
'description', 'tags',
]
help_texts = {
'port_speed': _("Physical circuit speed"),

View File

@ -391,7 +391,7 @@ class SiteView(generic.ObjectView):
scope_id=instance.pk
).count(),
'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=instance).count(),
'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).count(),
'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct().count(),
'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance).count(),
}
locations = Location.objects.add_related_count(

View File

@ -21,6 +21,8 @@ from extras.choices import *
from extras.constants import *
from extras.conditions import ConditionSet
from extras.utils import FeatureQuery, image_upload
from netbox.config import get_config
from netbox.constants import RQ_QUEUE_DEFAULT
from netbox.models import ChangeLoggedModel
from netbox.models.features import (
CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, JobResultsMixin, TagsMixin, WebhooksMixin,
@ -679,7 +681,8 @@ class JobResult(models.Model):
schedule_at: Schedule the job to be executed at the passed date and time
interval: Recurrence interval (in minutes)
"""
queue = django_rq.get_queue('default')
rq_queue_name = get_config().QUEUE_MAPPINGS.get(obj_type.name, RQ_QUEUE_DEFAULT)
queue = django_rq.get_queue(rq_queue_name)
status = JobResultStatusChoices.STATUS_SCHEDULED if schedule_at else JobResultStatusChoices.STATUS_PENDING
job_result: JobResult = JobResult.objects.create(
name=name,

View File

@ -216,7 +216,8 @@ class ObjectChangeTable(NetBoxTable):
object_repr = tables.TemplateColumn(
accessor=tables.A('changed_object'),
template_code=OBJECTCHANGE_OBJECT,
verbose_name='Object'
verbose_name='Object',
orderable=False
)
request_id = tables.TemplateColumn(
template_code=OBJECTCHANGE_REQUEST_ID,

View File

@ -5,6 +5,8 @@ from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
from django_rq import get_queue
from netbox.config import get_config
from netbox.constants import RQ_QUEUE_DEFAULT
from netbox.registry import registry
from utilities.api import get_serializer_for_model
from utilities.utils import serialize_object
@ -78,7 +80,8 @@ def flush_webhooks(queue):
"""
Flush a list of object representation to RQ for webhook processing.
"""
rq_queue = get_queue('default')
rq_queue_name = get_config().QUEUE_MAPPINGS.get('webhook', RQ_QUEUE_DEFAULT)
rq_queue = get_queue(rq_queue_name)
webhooks_cache = {
'type_create': {},
'type_update': {},

View File

@ -962,7 +962,7 @@ class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta:
model = L2VPN
fields = ['id', 'identifier', 'name', 'type', 'description']
fields = ['id', 'identifier', 'name', 'slug', 'type', 'description']
def search(self, queryset, name, value):
if not value.strip():

View File

@ -1505,6 +1505,10 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'name': ['L2VPN 1', 'L2VPN 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_slug(self):
params = {'slug': ['l2vpn-1', 'l2vpn-2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_identifier(self):
params = {'identifier': ['65001', '65002']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

View File

@ -1,2 +1,7 @@
# Prefix for nested serializers
NESTED_SERIALIZER_PREFIX = 'Nested'
# RQ queue names
RQ_QUEUE_DEFAULT = 'default'
RQ_QUEUE_HIGH = 'high'
RQ_QUEUE_LOW = 'low'

View File

@ -17,6 +17,7 @@ from extras.plugins import PluginConfig
from sentry_sdk.integrations.django import DjangoIntegration
from netbox.config import PARAMS
from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW
#
@ -101,6 +102,7 @@ MEDIA_ROOT = getattr(configuration, 'MEDIA_ROOT', os.path.join(BASE_DIR, 'media'
METRICS_ENABLED = getattr(configuration, 'METRICS_ENABLED', False)
PLUGINS = getattr(configuration, 'PLUGINS', [])
PLUGINS_CONFIG = getattr(configuration, 'PLUGINS_CONFIG', {})
QUEUE_MAPPINGS = getattr(configuration, 'QUEUE_MAPPINGS', {})
RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None)
REMOTE_AUTH_AUTO_CREATE_USER = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_USER', False)
REMOTE_AUTH_BACKEND = getattr(configuration, 'REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend')
@ -639,11 +641,16 @@ else:
}
RQ_QUEUES = {
'high': RQ_PARAMS,
'default': RQ_PARAMS,
'low': RQ_PARAMS,
RQ_QUEUE_HIGH: RQ_PARAMS,
RQ_QUEUE_DEFAULT: RQ_PARAMS,
RQ_QUEUE_LOW: RQ_PARAMS,
}
# Add any queues defined in QUEUE_MAPPINGS
RQ_QUEUES.update({
queue: RQ_PARAMS for queue in set(QUEUE_MAPPINGS.values()) if queue not in RQ_QUEUES
})
#
# Plugins

View File

@ -102,14 +102,14 @@ Blocks:
{% block content %}{% endblock %}
</div>
{% endblock %}
{# Bottom banner #}
{% if config.BANNER_BOTTOM %}
<div class="text-center mx-3">
{{ config.BANNER_BOTTOM|safe }}
</div>
{% endif %}
</div>
{% if config.BANNER_BOTTOM %}
<div class="text-center mx-3">
{{ config.BANNER_BOTTOM|safe }}
</div>
{% endif %}
{# BS5 pop-up modals #}
{% block modals %}{% endblock %}

View File

@ -32,6 +32,7 @@
{% render_field form.site %}
</div>
<div class="tab-pane{% if providernetwork_tab_active %} active{% endif %}" id="providernetwork">
{% render_field form.provider_network_provider %}
{% render_field form.provider_network %}
</div>
</div>

View File

@ -81,7 +81,7 @@
{% else %}
<tr>
<td>Provider Network</td>
<td>{{ termination.provider_network|linkify }}</td>
<td>{{ termination.provider_network.provider|linkify }} / {{ termination.provider_network|linkify }}</td>
</tr>
{% endif %}
<tr>

View File

@ -140,7 +140,8 @@ def percentage(x, y):
"""
if x is None or y is None:
return None
return round(x / y * 100)
return round(x / y * 100, 1)
@register.filter()