Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initiating Nautobot jobs, next-2.0 rebase #270

Merged
merged 29 commits into from
Mar 13, 2024

Conversation

MeganerdDev
Copy link
Contributor

@MeganerdDev MeganerdDev commented Sep 26, 2023

Closes: #223

#224 closed in favor of this rebase

What's Changed

Adds the following nautobot subcommands:

  • init_job() as init-job

    • Adds init_job subccomand which initiates a Nautobot job by job name
    • Update init_jobs for new pattern, tested and working
    • Adds kwargs JSON-string dictionary to init_jobs (untested but looks good)
  • get_jobs() as get-jobs

    • Adds get_jobs which returns all Nautobot jobs viewable to user
    • Adds kwargs JSON-string list to get_jobs export header items (tested as working)
  • filter_jobs() as filter-jobs

    • Adds filter_jobs which returns filtered set of Nautobot jobs viewable to user
      • eg: /nautobot filter-jobs "enabled" or /nautobot filter-jobs "enabled,installed"
        • this could be changed to JSON-string dictionary/list? eg: /nautobot filter-jobs '{"enabled": true}'

Remaining work

  • The render markdown for init_job should presen thet job form items
    • With init_job method we can pass kwargs as JSON-string (untested)
  • Add to documentation

TODO

  • Explanation of Change(s)
  • Added change log fragment(s) (for more information see the documentation)
  • Attached Screenshots, Payload Example
  • Unit, Integration Tests
  • Documentation Updates (when adding/changing features)
  • Outline Remaining Work, Constraints from Design

Screenshots

/nautobot init-job

image

/nautobot init-jobs for job that is not enabled

image

if there is a failed job, we will return the job_result.result message
image

/nautobot get-jobs

by default we will get some head items
image

/nautobot get-jobs '["id", "name", "installed", "tags"]'

we can specify the header items we want with a JSON formatted list
image
image

if there is a bad key, eg: /nautobot get-jobs '["id", "name", "invalid key demo"]'
image

/nautobot filter-jobs "enabled"

image

Notes

  • I was having some GIT troubles rebasing to next-2.0 the exact way I wanted, which is why I opened this PR. The other PR may serve use for any 1.x hold-overs by using the static service account?
  • Thanks for the hard work on the user permission mapping @smk4664 , the feature looks solid

meganerd added 2 commits September 25, 2023 20:04
Update init_jobs for new pattern, tested and working
Add kwargs to init_jobs, untested but looks good
Add get_jobs which returns all Nautobot jobs viewable to user
Add filter_jobs which returns filtered set of Nautobot jobs viewable to user
@MeganerdDev MeganerdDev mentioned this pull request Sep 26, 2023
6 tasks
@MeganerdDev
Copy link
Contributor Author

Got some edge cases left I think, but pretty much everything but making CI happy is left

@MeganerdDev
Copy link
Contributor Author

I left a few TODO entries, but nothing is a blocker I think

@MeganerdDev
Copy link
Contributor Author

Not sure if making pylint happy for generator is mandatory for merge or if we can add an ignore for this case
# pylint: disable=use-a-generator

profile = True

# Get instance of the user who will run the job
user = get_user_model()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do not need to get the user model, or get the user, as dispatcher.user is already a user instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, pushed d7e4ccc

return (CommandStatusChoices.STATUS_FAILED, f'Job "{job_name}" failed to initiate. Result: {job_result.result}')

# TODO: need base-domain, this yields: /extras/job-results/<job_id>/
job_url = job_result.get_absolute_url()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use dispatcher.context['request_schema'] and dispatcher.context['request_host'] to get the base-domain. But this won't work in Slack Socket Mode. I added the SLACK_SOCKET_STATIC_HOST to solve that issue for static urls, but since static could point to S3, we would need another solution here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noted, will explore this


job_class_path = job_model.class_path

# TODO: Check if json_args keys are valid for this job model
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might also be a good idea to ensure the user has permissions to run the job.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the restrict() a few lines up do this? I can test to confirm

job_model = Job.objects.restrict(dispatcher.user, "view").get(name=job_name)```

@MeganerdDev
Copy link
Contributor Author

Thanks for the feedback, Ill get this updated!

@MeganerdDev
Copy link
Contributor Author

MeganerdDev commented Sep 29, 2023

My current action/to-do items for this PR:

  • have a job's user-input widgets prompt for chat user input
  • job to provide link to job (base url + path)
  • testing to confirm features working as expected
  • documentation tasks

@MeganerdDev
Copy link
Contributor Author

status update on TODO item -> "have a job's user-input widgets prompt for chat user input"
I have a working concept, will update PR with that soon!

@MeganerdDev
Copy link
Contributor Author

This now provides a separate nautobot subcommand init-job-form

When /nautobot init-job-form is called without a job name it presents a drop-down dialog
Otherwise you can specify the job name, eg: /nautobot init-job-form "demo-job"
image
image

image

Caveats:

  • Currently there is a blocking issues I am stuck on for this. Whenever I initiate the job it goes from Running then Pending status, although the job is actually running.
  • init-job-form could be merged to a single subcommand with init-job, have the input form call itself with the additional args

meganerd and others added 6 commits October 31, 2023 00:54
prompt_for_job added to init-job subcommand
removed testing code blocks
todo: bug: single job forms wont submit properly with multi_input_dialog
notes: merge init-job and init-job-form seems best
…attern

Added wait for job to initiate using refresh_from_db
Corrected the initiated job url hyperlink message
@MeganerdDev
Copy link
Contributor Author

I removed this helper function, noting in case we want to re-add (eg: enable_job subcommand would use this)

def enable_job(module, name, source="local"):
    """
    Helper function to look up a job by class and job model and enable it.

    Args:
        module (str): Job module name
        name (str): Job class name
        source (str): Job grouping (default: "local")
    """
    job_class = get_job(f"{module}.{name}")
    job_model = Job.objects.get(module_name=module, job_class_name=name) # .restrict() may be preferred if we pass user
    if not job_model.enabled:
        job_model.enabled = True
        job_model.validated_save()
    return 

@MeganerdDev
Copy link
Contributor Author

Noting some things for the current status of this PR:

  • I explored the idea of pagination of the job's drop-down item. It seemed to me that widget in each dispatcher already supported a large enough volume for these items
  • Combining init_job and init_job_form subcommands? Not required for PR, but may be the better pattern
  • Add an enable_job subcommand using the helper above? Not required for PR, can be its own PR
  • Numeric input mask for numeric only text input widgets on client-side? Unexplored
  • Some remaining TODO comments can be removed from the PR

Fixed items:

  • The correct Job URL hyperlink is now properly presented to user in chat response
  • The job is properly initiating, corrected by enqueue_job and refresh_from_db

Follow existing pylint ignore pattern on repo
)

# Wait on the job to finish
while job_result.status not in JobResultStatusChoices.READY_STATES:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want something to drop out of this loop after enough time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want something to drop out of this loop after enough time?

Addressed in 7470ec8



@subcommand_of("nautobot")
def init_job(dispatcher, *args, job_name: str = "", json_string_kwargs: str = ""): # pylint: disable=too-many-locals
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this make more sense to be named start_job or maybe launch_job? And then as I put that into suggestion, I then looked at Nautobot itself. Probably run_job would make sense to line up with the Run Job button.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_job should be fine for the namespace. Initially I did not use run_job namespace since there was a similar import nautobot.extras.jobs import run_job, but this has been replaced by enqueue_job anyways in the PR

Rename `init_job_form`subcommand to `run_job_form`

if job_result and job_result.status == "FAILURE":
dispatcher.send_error(f"The requested job {job_name} failed to initiate. Result: {job_result.result}")
return (CommandStatusChoices.STATUS_FAILED, f'Job "{job_name}" failed to initiate. Result: {job_result.result}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checking if the task failed, but then sending a message that the task failed to initiate. The task was initiated but failed.

Copy link
Contributor Author

@MeganerdDev MeganerdDev Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I adjusted the phrasing in 875ca64, but let me know if you have anything specific in mind maybe

@qduk
Copy link
Contributor

qduk commented Mar 1, 2024

Awesome work @MeganerdDev! The only thing I'd note is the addition of some documentation in the /nautobot commands table.

@smk4664 smk4664 merged commit eb31930 into nautobot:next-2.0 Mar 13, 2024
14 checks passed
@smk4664 smk4664 mentioned this pull request Mar 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants