From ad08eafb9a8ed775dc0cf21eb38e443651e11184 Mon Sep 17 00:00:00 2001 From: Elijah DeLee Date: Thu, 30 Jun 2022 23:37:28 -0400 Subject: [PATCH] add debug views for task manager(s) implement https://github.com/ansible/awx/issues/12446 in development environment, enable set of views that run the task manager(s). Also introduce a setting that disables any calls to schedule() that do not originate from the debug views when in the development environment. With guards around both if we are in the development environment and the setting, I think we're pretty safe this won't get triggered unintentionally. use MODE to determine if we are in devel env Also, move test for skipping task managers to the tasks file --- awx/api/urls/debug.py | 17 ++++++++++ awx/api/urls/urls.py | 8 ++++- awx/api/views/debug.py | 68 +++++++++++++++++++++++++++++++++++++ awx/main/scheduler/tasks.py | 20 +++++++++++ awx/settings/development.py | 7 ++++ 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 awx/api/urls/debug.py create mode 100644 awx/api/views/debug.py diff --git a/awx/api/urls/debug.py b/awx/api/urls/debug.py new file mode 100644 index 0000000000..30eb6d08b3 --- /dev/null +++ b/awx/api/urls/debug.py @@ -0,0 +1,17 @@ +from django.urls import re_path + +from awx.api.views.debug import ( + DebugRootView, + TaskManagerDebugView, + DependencyManagerDebugView, + WorkflowManagerDebugView, +) + +urls = [ + re_path(r'^$', DebugRootView.as_view(), name='debug'), + re_path(r'^task_manager/$', TaskManagerDebugView.as_view(), name='task_manager'), + re_path(r'^dependency_manager/$', DependencyManagerDebugView.as_view(), name='dependency_manager'), + re_path(r'^workflow_manager/$', WorkflowManagerDebugView.as_view(), name='workflow_manager'), +] + +__all__ = ['urls'] diff --git a/awx/api/urls/urls.py b/awx/api/urls/urls.py index c092696d24..96c57e97c9 100644 --- a/awx/api/urls/urls.py +++ b/awx/api/urls/urls.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, unicode_literals from django.conf import settings from django.urls import include, re_path +from awx import MODE from awx.api.generics import LoggedLoginView, LoggedLogoutView from awx.api.views import ( ApiRootView, @@ -145,7 +146,12 @@ urlpatterns = [ re_path(r'^logout/$', LoggedLogoutView.as_view(next_page='/api/', redirect_field_name='next'), name='logout'), re_path(r'^o/', include(oauth2_root_urls)), ] -if settings.SETTINGS_MODULE == 'awx.settings.development': +if MODE == 'development': + # Only include these if we are in the development environment from awx.api.swagger import SwaggerSchemaView urlpatterns += [re_path(r'^swagger/$', SwaggerSchemaView.as_view(), name='swagger_view')] + + from awx.api.urls.debug import urls as debug_urls + + urlpatterns += [re_path(r'^debug/', include(debug_urls))] diff --git a/awx/api/views/debug.py b/awx/api/views/debug.py new file mode 100644 index 0000000000..13dfc4a604 --- /dev/null +++ b/awx/api/views/debug.py @@ -0,0 +1,68 @@ +from collections import OrderedDict + +from django.conf import settings + +from rest_framework.permissions import AllowAny +from rest_framework.response import Response +from rest_framework.views import APIView + +from awx.main.scheduler import TaskManager, DependencyManager, WorkflowManager + + +class TaskManagerDebugView(APIView): + _ignore_model_permissions = True + exclude_from_schema = True + permission_classes = [AllowAny] + prefix = 'Task' + + def get(self, request): + TaskManager().schedule() + if not settings.AWX_DISABLE_TASK_MANAGERS: + msg = f"Running {self.prefix} manager. To disable other triggers to the {self.prefix} manager, set AWX_DISABLE_TASK_MANAGERS to True" + else: + msg = f"AWX_DISABLE_TASK_MANAGERS is True, this view is the only way to trigger the {self.prefix} manager" + return Response(msg) + + +class DependencyManagerDebugView(APIView): + _ignore_model_permissions = True + exclude_from_schema = True + permission_classes = [AllowAny] + prefix = 'Dependency' + + def get(self, request): + DependencyManager().schedule() + if not settings.AWX_DISABLE_TASK_MANAGERS: + msg = f"Running {self.prefix} manager. To disable other triggers to the {self.prefix} manager, set AWX_DISABLE_TASK_MANAGERS to True" + else: + msg = f"AWX_DISABLE_TASK_MANAGERS is True, this view is the only way to trigger the {self.prefix} manager" + return Response(msg) + + +class WorkflowManagerDebugView(APIView): + _ignore_model_permissions = True + exclude_from_schema = True + permission_classes = [AllowAny] + prefix = 'Workflow' + + def get(self, request): + WorkflowManager().schedule() + if not settings.AWX_DISABLE_TASK_MANAGERS: + msg = f"Running {self.prefix} manager. To disable other triggers to the {self.prefix} manager, set AWX_DISABLE_TASK_MANAGERS to True" + else: + msg = f"AWX_DISABLE_TASK_MANAGERS is True, this view is the only way to trigger the {self.prefix} manager" + return Response(msg) + + +class DebugRootView(APIView): + _ignore_model_permissions = True + exclude_from_schema = True + permission_classes = [AllowAny] + + def get(self, request, format=None): + '''List of available debug urls''' + data = OrderedDict() + data['task_manager'] = '/api/debug/task_manager/' + data['dependency_manager'] = '/api/debug/dependency_manager/' + data['workflow_manager'] = '/api/debug/workflow_manager/' + return Response(data) diff --git a/awx/main/scheduler/tasks.py b/awx/main/scheduler/tasks.py index 83d53185a2..307f0a7d69 100644 --- a/awx/main/scheduler/tasks.py +++ b/awx/main/scheduler/tasks.py @@ -1,7 +1,11 @@ # Python import logging +# Django +from django.conf import settings + # AWX +from awx import MODE from awx.main.scheduler import TaskManager, DependencyManager, WorkflowManager from awx.main.dispatch.publish import task from awx.main.dispatch import get_local_queuename @@ -11,20 +15,36 @@ logger = logging.getLogger('awx.main.scheduler') @task(queue=get_local_queuename) def task_manager(): + prefix = 'task' + if MODE == 'development' and settings.AWX_DISABLE_TASK_MANAGERS: + logger.debug(f"Not running {prefix} manager, AWX_DISABLE_TASK_MANAGERS is True. Trigger with GET to /api/debug/{prefix}_manager/") + return + TaskManager().schedule() @task(queue=get_local_queuename) def dependency_manager(): + prefix = 'dependency' + if MODE == 'development' and settings.AWX_DISABLE_TASK_MANAGERS: + logger.debug(f"Not running {prefix} manager, AWX_DISABLE_TASK_MANAGERS is True. Trigger with GET to /api/debug/{prefix}_manager/") + return DependencyManager().schedule() @task(queue=get_local_queuename) def workflow_manager(): + prefix = 'workflow' + if MODE == 'development' and settings.AWX_DISABLE_TASK_MANAGERS: + logger.debug(f"Not running {prefix} manager, AWX_DISABLE_TASK_MANAGERS is True. Trigger with GET to /api/debug/{prefix}_manager/") + return WorkflowManager().schedule() def run_task_manager(): + if MODE == 'development' and settings.AWX_DISABLE_TASK_MANAGERS: + logger.debug(f"Not running task managers, AWX_DISABLE_TASK_MANAGERS is True. Trigger with GET to /api/debug/{prefix}_manager/") + return task_manager() dependency_manager() workflow_manager() diff --git a/awx/settings/development.py b/awx/settings/development.py index be1c115606..c5b5ab1a36 100644 --- a/awx/settings/development.py +++ b/awx/settings/development.py @@ -110,5 +110,12 @@ CLUSTER_HOST_ID = socket.gethostname() AWX_CALLBACK_PROFILE = True +# ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= +# Disable normal scheduled/triggered task managers (DependencyManager, TaskManager, WorkflowManager). +# Allows user to trigger task managers directly for debugging and profiling purposes. +# Only works in combination with settings.SETTINGS_MODULE == 'awx.settings.development' +AWX_DISABLE_TASK_MANAGERS = os.getenv('AWX_DISABLE_TASK_MANAGERS', False) +# ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= + if 'sqlite3' not in DATABASES['default']['ENGINE']: # noqa DATABASES['default'].setdefault('OPTIONS', dict()).setdefault('application_name', f'{CLUSTER_HOST_ID}-{os.getpid()}-{" ".join(sys.argv)}'[:63]) # noqa