Skip to content

Migrating Jobs from Nautobot v1.X to Nautobot v2.0

Quick Summary of Job Class Changes

Overview

Some fundamental changes were made to Jobs in Nautobot v2.0. This document outlines the changes that were made and how to migrate your existing Jobs to work in Nautobot v2.0. For more information about the changes made to the Job class and Job model in Nautobot v2.0 see the Upgrading From Nautobot v1 documentation.

Job Package Names

All Jobs are now imported as normal Python packages, instead of virtually imported, which means that Job file code can be shared with other Jobs or Python modules.

App Provided Jobs

The package name for Jobs provided by Nautobot Apps has not changed.

Jobs in JOBS_ROOT

JOBS_ROOT is added to sys.path and all modules in that directory will be imported. The package name for Jobs in JOBS_ROOT will be <job_file>, where <job_file> is the name of the Job file without the .py extension. If desired, submodules may be used in JOBS_ROOT like any normal Python package. For example, a Job class called AddJob in $JOBS_ROOT/my_jobs/math.py would be imported as my_jobs.math.AddJob.

Caution

Take care to avoid naming collisions with existing Python packages when naming Job files in JOBS_ROOT.

Git Repository Jobs

The package name for Jobs provided by Git Repositories has changed to <git_repository_slug>.jobs, where <git_repository_slug> is the slug of the Git Repository as provided by the user when creating the Git Repository object in Nautobot. All jobs provided by Git Repositories must use the .jobs submodule of the Git Repository.

Important

As a result of the changes to the way jobs are imported the top-level directory of any Git Repository that provides Jobs must now contain an __init__.py file.

Run Method Signature

The signature of the run() method for Jobs must now accept keyword arguments for every Job variable defined on the Job class. The run() method no longer uses the data and commit arguments used in v1.X.

Example

v1.X Job
class AddJob(Job):
    a = IntegerVar()
    b = IntegerVar()

    def run(self, data, commit):
        return data["a"] + data["b"]
v2.0 Job
class AddJob(Job):
    a = IntegerVar()
    b = IntegerVar()

    def run(self, a, b):
        return a + b
v2.0 Job - Easy Migration
class AddJob(Job):
    a = IntegerVar()
    b = IntegerVar()

    def run(self, **data):
        return data["a"] + data["b"]

test_* and post_run() Methods

The test_* and post_run methods, previously provided for backwards compatibility to NetBox scripts and reports, have been removed. Celery implements before_start, on_success, on_retry, on_failure, and after_return methods that can be used by Job authors to perform similar functions.

Important

Be sure to call the super() method when overloading any of the job's before_start, on_success, on_retry, on_failure, or after_return methods

Database Transaction Handling

Jobs no longer run in a single atomic database transaction by default. If a Job needs to run in a database transaction, you can use the @transaction.atomic decorator on the run() method or wrap parts of your Job code in the with transaction.atomic() context manager.

Example

from django.db import transaction
from nautobot.apps.jobs import Job, ObjectVar
from nautobot.dcim import models

class UpdateDeviceTypeHeightJob(Job):
    device_type = ObjectVar(model=models.DeviceType)
    u_height = IntegerVar(label="New Height (U)")

    @transaction.atomic
    def run(self, device_type, u_height):
        device_type.u_height = u_height
        device_type.save()

Commit Argument

As a result of the default database transaction being removed from Nautobot core, the commit argument has been removed. If a Job author wants to provide users the ability to bypass approval when approval_required is set, the Job must implement a dryrun variable using the newly-introduced DryRunVar. The desired value of the variable will be passed to the run method just like any other variable, but the Job author must implement the logic to handle the dry run.

The presence of a dryrun = DryRunVar() property on the Job class sets the supports_dryrun flag on the Job model, which allows users to bypass approval when approval_required is set. To implement a dry run variable without allowing users to bypass approval, the dryrun variable should use the BooleanVar class instead of DryRunVar.

The Job commit_default property has been renamed to dryrun_default and the default value as well as any existing database values have been flipped.

Read-Only Meta Attribute

The read_only Job field no longer forces an automatic database rollback at the end of the Job; it is informational only in v2.0.

Job Registration

Changed in version 2.0.0

All Job classes, including JobHookReceiver and JobButtonReceiver classes must be registered at import time using the nautobot.apps.jobs.register_jobs method. This method accepts one or more job classes as arguments. You must account for how your jobs are imported when deciding where to call this method.

Registering Jobs in JOBS_ROOT or Git Repositories

Only top level module names within JOBS_ROOT are imported by Nautobot at runtime. This means that if you're using submodules, you need to ensure that your jobs are either registered in your top level __init__.py or that this file imports your submodules where the jobs are registered:

$JOBS_ROOT/my_jobs/__init__.py
from . import my_job_module
$JOBS_ROOT/my_jobs/my_job_module.py
from nautobot.apps.jobs import Job, register_jobs

class MyJob(Job):
    ...

register_jobs(MyJob)

Similarly, only the jobs module is loaded from Git repositories. If you're using submodules, you need to ensure that your jobs are either registered in the repository's jobs/__init__.py or that this file imports your submodules where the jobs are registered.

If not using submodules, you should register your job in the file where your job is defined.

Registering Jobs in an App

Apps should register jobs in the module defined in their NautobotAppConfig.jobs property. This defaults to the jobs module of the App.

Job Logging

All of the custom log methods have been removed from the Job class. Instead, use either the Python logger from nautobot.extras.jobs.get_task_logger or use self.logger in the Job class.

The following table describes the mapping of the old log methods to the new methods:

Old Method New Method
self.log_debug self.logger.debug
self.log_info self.logger.info
self.log_success self.logger.info
self.log_warning self.logger.warning
self.log_failure self.logger.error
self.log self.logger.info
No equivalent self.logger.critical

Some logging features from v1.X are accessible when passing a dict to the extra kwarg in any logger method:

  • logger.debug("message", extra={"skip_db_logging": True}) - Log the message to the console but not to the database
  • logger.debug("message", extra={"object": obj}) - Replaces the obj kwarg in Nautobot v1.X Job logging methods
  • logger.debug("message", extra={"grouping": "string"}) - Replaces the active_test Job property in Nautobot v1.X

For more detailed documentation on Job logging see the Job Logging section of the Jobs feature documentation.

Tracking Job State

JobResult.status is now automatically tracked by Celery. Job authors should no longer manually change self.job_result.status or self.job_result.completed and should instead raise an exception if the Job status should be set to failed (the failed status is now "FAILURE").

The Job's built-inself.failed flag, that was used to determine if a Job failed, has been removed. This flag was previously set to True automatically when the log_failure method was called. Job authors should track their Job's internal state and raise an exception to fail the Job when desired.

Request Property

The request property has been changed to a Celery request instead of a Django request and no longer includes the information from the web request that initiated the Job. The user object is now available as self.user instead of self.request.user.

Note: Migrating from v1.x to v2.0 provides a general migration guide.