mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
#11558: Disable sync button if RQ worker not running
This commit is contained in:
parent
13d604d44e
commit
08bdb54cb4
@ -16,7 +16,6 @@ from django.utils.translation import gettext as _
|
|||||||
|
|
||||||
from extras.models import JobResult
|
from extras.models import JobResult
|
||||||
from netbox.models import PrimaryModel
|
from netbox.models import PrimaryModel
|
||||||
from netbox.models.features import ChangeLoggingMixin
|
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
from utilities.files import sha256_hash
|
from utilities.files import sha256_hash
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
@ -116,6 +115,7 @@ class DataSource(PrimaryModel):
|
|||||||
"""
|
"""
|
||||||
# Set the status to "syncing"
|
# Set the status to "syncing"
|
||||||
self.status = DataSourceStatusChoices.QUEUED
|
self.status = DataSourceStatusChoices.QUEUED
|
||||||
|
DataSource.objects.filter(pk=self.pk).update(status=self.status)
|
||||||
|
|
||||||
# Enqueue a sync job
|
# Enqueue a sync job
|
||||||
job_result = JobResult.enqueue_job(
|
job_result = JobResult.enqueue_job(
|
||||||
@ -137,8 +137,8 @@ class DataSource(PrimaryModel):
|
|||||||
"""
|
"""
|
||||||
Create/update/delete child DataFiles as necessary to synchronize with the remote source.
|
Create/update/delete child DataFiles as necessary to synchronize with the remote source.
|
||||||
"""
|
"""
|
||||||
if not self.ready_for_sync:
|
if self.status == DataSourceStatusChoices.SYNCING:
|
||||||
raise SyncError(f"Cannot initiate sync; data source not ready/enabled")
|
raise SyncError(f"Cannot initiate sync; syncing already in progress.")
|
||||||
|
|
||||||
# Emit the pre_sync signal
|
# Emit the pre_sync signal
|
||||||
pre_sync.send(sender=self.__class__, instance=self)
|
pre_sync.send(sender=self.__class__, instance=self)
|
||||||
|
@ -3,6 +3,7 @@ from django.shortcuts import get_object_or_404, redirect
|
|||||||
|
|
||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from netbox.views.generic.base import BaseObjectView
|
from netbox.views.generic.base import BaseObjectView
|
||||||
|
from utilities.rqworker import get_queue_for_model, get_workers_for_queue
|
||||||
from utilities.utils import count_related
|
from utilities.utils import count_related
|
||||||
from utilities.views import register_model_view
|
from utilities.views import register_model_view
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
@ -31,7 +32,11 @@ class DataSourceView(generic.ObjectView):
|
|||||||
(DataFile.objects.restrict(request.user, 'view').filter(source=instance), 'source_id'),
|
(DataFile.objects.restrict(request.user, 'view').filter(source=instance), 'source_id'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
queue_name = get_queue_for_model(DataSource)
|
||||||
|
sync_enabled = bool(get_workers_for_queue(queue_name))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
'sync_enabled': sync_enabled,
|
||||||
'related_models': related_models,
|
'related_models': related_models,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ from netbox.models.features import (
|
|||||||
TagsMixin, WebhooksMixin,
|
TagsMixin, WebhooksMixin,
|
||||||
)
|
)
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
|
from utilities.rqworker import get_queue_for_model
|
||||||
from utilities.utils import render_jinja2
|
from utilities.utils import render_jinja2
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -730,7 +731,7 @@ class JobResult(models.Model):
|
|||||||
schedule_at: Schedule the job to be executed at the passed date and time
|
schedule_at: Schedule the job to be executed at the passed date and time
|
||||||
interval: Recurrence interval (in minutes)
|
interval: Recurrence interval (in minutes)
|
||||||
"""
|
"""
|
||||||
rq_queue_name = get_config().QUEUE_MAPPINGS.get(obj_type.model, RQ_QUEUE_DEFAULT)
|
rq_queue_name = get_queue_for_model(obj_type.model)
|
||||||
queue = django_rq.get_queue(rq_queue_name)
|
queue = django_rq.get_queue(rq_queue_name)
|
||||||
status = JobResultStatusChoices.STATUS_SCHEDULED if schedule_at else JobResultStatusChoices.STATUS_PENDING
|
status = JobResultStatusChoices.STATUS_SCHEDULED if schedule_at else JobResultStatusChoices.STATUS_PENDING
|
||||||
job_result: JobResult = JobResult.objects.create(
|
job_result: JobResult = JobResult.objects.create(
|
||||||
|
@ -6,14 +6,13 @@ from django.http import Http404, HttpResponseBadRequest, HttpResponseForbidden,
|
|||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django_rq.queues import get_connection
|
|
||||||
from rq import Worker
|
|
||||||
|
|
||||||
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
|
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
|
||||||
from extras.dashboard.utils import get_widget_class
|
from extras.dashboard.utils import get_widget_class
|
||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from utilities.forms import ConfirmationForm, get_field_value
|
from utilities.forms import ConfirmationForm, get_field_value
|
||||||
from utilities.htmx import is_htmx
|
from utilities.htmx import is_htmx
|
||||||
|
from utilities.rqworker import get_workers_for_queue
|
||||||
from utilities.templatetags.builtins.filters import render_markdown
|
from utilities.templatetags.builtins.filters import render_markdown
|
||||||
from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict
|
from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict
|
||||||
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
|
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
|
||||||
@ -863,7 +862,7 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
||||||
# Allow execution only if RQ worker process is running
|
# Allow execution only if RQ worker process is running
|
||||||
if not Worker.count(get_connection('default')):
|
if not get_workers_for_queue('default'):
|
||||||
messages.error(request, "Unable to run report: RQ worker process not running.")
|
messages.error(request, "Unable to run report: RQ worker process not running.")
|
||||||
return render(request, 'extras/report.html', {
|
return render(request, 'extras/report.html', {
|
||||||
'report': report,
|
'report': report,
|
||||||
@ -994,7 +993,7 @@ class ScriptView(ContentTypePermissionRequiredMixin, GetScriptMixin, View):
|
|||||||
form = script.as_form(request.POST, request.FILES)
|
form = script.as_form(request.POST, request.FILES)
|
||||||
|
|
||||||
# Allow execution only if RQ worker process is running
|
# Allow execution only if RQ worker process is running
|
||||||
if not Worker.count(get_connection('default')):
|
if not get_workers_for_queue('default'):
|
||||||
messages.error(request, "Unable to run script: RQ worker process not running.")
|
messages.error(request, "Unable to run script: RQ worker process not running.")
|
||||||
|
|
||||||
elif form.is_valid():
|
elif form.is_valid():
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
{% block extra_controls %}
|
{% block extra_controls %}
|
||||||
{% if perms.core.sync_datasource %}
|
{% if perms.core.sync_datasource %}
|
||||||
{% if object.ready_for_sync %}
|
{% if sync_enabled and object.ready_for_sync %}
|
||||||
<form action="{% url 'core:datasource_sync' pk=object.pk %}" method="post">
|
<form action="{% url 'core:datasource_sync' pk=object.pk %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn btn-sm btn-primary">
|
<button type="submit" class="btn btn-sm btn-primary">
|
||||||
@ -14,9 +14,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-primary" disabled>
|
<span class="inline-block" tabindex="0" data-bs-toggle="tooltip" data-bs-delay="100" data-bs-placement="bottom" title="Unable to sync: No RQ worker running">
|
||||||
<i class="mdi mdi-sync" aria-hidden="true"></i> Sync
|
<button class="btn btn-sm btn-primary" disabled>
|
||||||
</button>
|
<i class="mdi mdi-sync" aria-hidden="true"></i> Sync
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
24
netbox/utilities/rqworker.py
Normal file
24
netbox/utilities/rqworker.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from django_rq.queues import get_connection
|
||||||
|
from rq import Worker
|
||||||
|
|
||||||
|
from netbox.config import get_config
|
||||||
|
from netbox.constants import RQ_QUEUE_DEFAULT
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'get_queue_for_model',
|
||||||
|
'get_workers_for_queue',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_queue_for_model(model):
|
||||||
|
"""
|
||||||
|
Return the configured queue name for jobs associated with the given model.
|
||||||
|
"""
|
||||||
|
return get_config().QUEUE_MAPPINGS.get(model, RQ_QUEUE_DEFAULT)
|
||||||
|
|
||||||
|
|
||||||
|
def get_workers_for_queue(queue_name):
|
||||||
|
"""
|
||||||
|
Returns True if a worker process is currently servicing the specified queue.
|
||||||
|
"""
|
||||||
|
return Worker.count(get_connection(queue_name))
|
Loading…
Reference in New Issue
Block a user