Skip to content

Commit

Permalink
Relax requirement for Jobs to reference a specific object
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Jul 24, 2024
1 parent 62380fb commit d6432fb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 26 deletions.
24 changes: 24 additions & 0 deletions netbox/core/migrations/0012_job_object_type_optional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('core', '0011_move_objectchange'),
]

operations = [
migrations.AlterField(
model_name='job',
name='object_type',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='jobs',
to='contenttypes.contenttype'
),
),
]
14 changes: 10 additions & 4 deletions netbox/core/models/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class Job(models.Model):
to='contenttypes.ContentType',
related_name='jobs',
on_delete=models.CASCADE,
blank=True,
null=True
)
object_id = models.PositiveBigIntegerField(
blank=True,
Expand Down Expand Up @@ -197,27 +199,31 @@ def terminate(self, status=JobStatusChoices.STATUS_COMPLETED, error=None):
job_end.send(self)

@classmethod
def enqueue(cls, func, instance, name='', user=None, schedule_at=None, interval=None, run_now=False, **kwargs):
def enqueue(cls, func, instance=None, name='', user=None, schedule_at=None, interval=None, run_now=False, **kwargs):
"""
Create a Job instance and enqueue a job using the given callable
Args:
func: The callable object to be enqueued for execution
instance: The NetBox object to which this job pertains
instance: The NetBox object to which this job pertains (optional)
name: Name for the job (optional)
user: The user responsible for running the job
schedule_at: Schedule the job to be executed at the passed date and time
interval: Recurrence interval (in minutes)
run_now: Run the job immediately without scheduling it in the background. Should be used for interactive
management commands only.
"""
object_type = ObjectType.objects.get_for_model(instance, for_concrete_model=False)
if instance:
object_type = ObjectType.objects.get_for_model(instance, for_concrete_model=False)
object_id = instance.pk
else:
object_type = object_id = None
rq_queue_name = get_queue_for_model(object_type.model)
queue = django_rq.get_queue(rq_queue_name)
status = JobStatusChoices.STATUS_SCHEDULED if schedule_at else JobStatusChoices.STATUS_PENDING
job = Job.objects.create(
object_type=object_type,
object_id=instance.pk,
object_id=object_id,
name=name,
status=status,
scheduled=schedule_at,
Expand Down
25 changes: 3 additions & 22 deletions netbox/utilities/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,30 +161,11 @@ class SystemJob(ScheduledJob):
for system background tasks.
The main use case for this method is to schedule jobs programmatically instead of using user events, e.g. to start
jobs when the plugin is loaded in NetBox. For this purpose, the `setup()` method can be used to setup a new schedule
outside of the request-response cycle. It will register the new schedule right after all plugins are loaded and the
database is connected. Then `schedule()` will take care of scheduling a single job at a time.
jobs when the plugin is loaded in NetBox. For this purpose, the `setup()` method can be used to set up a new
schedule outside the request-response cycle. It will register the new schedule right after all plugins are loaded
and the database is connected. Then `schedule()` will take care of scheduling a single job at a time.
"""

@classmethod
def enqueue(cls, *args, **kwargs):
kwargs.pop('instance', None)
return super().enqueue(instance=Job(), *args, **kwargs)

@classmethod
def schedule(cls, *args, **kwargs):
kwargs.pop('instance', None)
return super().schedule(instance=Job(), *args, **kwargs)

@classmethod
def handle(cls, job, *args, **kwargs):
# A job requires a related object to be handled, or internal methods will fail. To avoid adding an extra model
# for this, the existing job object is used as a reference. This is not ideal, but it works for this purpose.
job.object = job
job.object_id = None # Hide changes from UI

super().handle(job, *args, **kwargs)

@classmethod
def setup(cls, *args, **kwargs):
"""
Expand Down

0 comments on commit d6432fb

Please sign in to comment.