From 8ec97235e39101ca1575d4df70cf3fd9e88ff1e4 Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 31 May 2019 13:46:45 -0400 Subject: [PATCH 01/15] Add feature for notifications to trigger on job start --- awx/api/serializers.py | 6 +++++ awx/api/urls/inventory_source.py | 3 +++ awx/api/urls/job_template.py | 3 +++ awx/api/urls/organization.py | 5 +++- awx/api/urls/project.py | 3 +++ awx/api/urls/system_job_template.py | 3 +++ awx/api/urls/workflow_job_template.py | 3 +++ awx/api/views/__init__.py | 38 +++++++++++++++++++++++++++ awx/api/views/organization.py | 8 ++++++ awx/main/models/ad_hoc_commands.py | 3 +++ awx/main/models/base.py | 6 +++++ awx/main/models/inventory.py | 5 ++++ awx/main/models/jobs.py | 12 ++++++++- awx/main/models/notifications.py | 11 +++++--- awx/main/models/projects.py | 8 ++++-- awx/main/models/workflow.py | 3 +++ awx/main/scheduler/task_manager.py | 2 +- awx/main/tasks.py | 4 ++- 18 files changed, 117 insertions(+), 9 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 4f57087599..7963c06f5a 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1247,6 +1247,7 @@ class OrganizationSerializer(BaseSerializer): activity_stream = self.reverse('api:organization_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates = self.reverse('api:organization_notification_templates_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:organization_notification_templates_any_list', kwargs={'pk': obj.pk}), + notification_templates_started = self.reverse('api:organization_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:organization_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:organization_notification_templates_error_list', kwargs={'pk': obj.pk}), object_roles = self.reverse('api:organization_object_roles_list', kwargs={'pk': obj.pk}), @@ -1353,6 +1354,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer): schedules = self.reverse('api:project_schedules_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:project_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:project_notification_templates_any_list', kwargs={'pk': obj.pk}), + notification_templates_started = self.reverse('api:project_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:project_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:project_notification_templates_error_list', kwargs={'pk': obj.pk}), access_list = self.reverse('api:project_access_list', kwargs={'pk': obj.pk}), @@ -1971,6 +1973,7 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt hosts = self.reverse('api:inventory_source_hosts_list', kwargs={'pk': obj.pk}), groups = self.reverse('api:inventory_source_groups_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:inventory_source_notification_templates_any_list', kwargs={'pk': obj.pk}), + notification_templates_started = self.reverse('api:inventory_source_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:inventory_source_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:inventory_source_notification_templates_error_list', kwargs={'pk': obj.pk}), )) @@ -2793,6 +2796,7 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO activity_stream = self.reverse('api:job_template_activity_stream_list', kwargs={'pk': obj.pk}), launch = self.reverse('api:job_template_launch', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:job_template_notification_templates_any_list', kwargs={'pk': obj.pk}), + notification_templates_started = self.reverse('api:job_template_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:job_template_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:job_template_notification_templates_error_list', kwargs={'pk': obj.pk}), access_list = self.reverse('api:job_template_access_list', kwargs={'pk': obj.pk}), @@ -3205,6 +3209,7 @@ class SystemJobTemplateSerializer(UnifiedJobTemplateSerializer): schedules = self.reverse('api:system_job_template_schedules_list', kwargs={'pk': obj.pk}), launch = self.reverse('api:system_job_template_launch', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:system_job_template_notification_templates_any_list', kwargs={'pk': obj.pk}), + notification_templates_started = self.reverse('api:system_job_template_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:system_job_template_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:system_job_template_notification_templates_error_list', kwargs={'pk': obj.pk}), @@ -3272,6 +3277,7 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo labels = self.reverse('api:workflow_job_template_label_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:workflow_job_template_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:workflow_job_template_notification_templates_any_list', kwargs={'pk': obj.pk}), + notification_templates_started = self.reverse('api:workflow_job_template_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:workflow_job_template_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:workflow_job_template_notification_templates_error_list', kwargs={'pk': obj.pk}), access_list = self.reverse('api:workflow_job_template_access_list', kwargs={'pk': obj.pk}), diff --git a/awx/api/urls/inventory_source.py b/awx/api/urls/inventory_source.py index 7a03b91f76..e20362b523 100644 --- a/awx/api/urls/inventory_source.py +++ b/awx/api/urls/inventory_source.py @@ -15,6 +15,7 @@ from awx.api.views import ( InventorySourceHostsList, InventorySourceNotificationTemplatesAnyList, InventorySourceNotificationTemplatesErrorList, + InventorySourceNotificationTemplatesStartedList, InventorySourceNotificationTemplatesSuccessList, ) @@ -31,6 +32,8 @@ urls = [ url(r'^(?P[0-9]+)/hosts/$', InventorySourceHostsList.as_view(), name='inventory_source_hosts_list'), url(r'^(?P[0-9]+)/notification_templates_any/$', InventorySourceNotificationTemplatesAnyList.as_view(), name='inventory_source_notification_templates_any_list'), + url(r'^(?P[0-9]+)/notification_templates_started/$', InventorySourceNotificationTemplatesStartedList.as_view(), + name='inventory_source_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', InventorySourceNotificationTemplatesErrorList.as_view(), name='inventory_source_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', InventorySourceNotificationTemplatesSuccessList.as_view(), diff --git a/awx/api/urls/job_template.py b/awx/api/urls/job_template.py index 0b43575ba4..d9c755e731 100644 --- a/awx/api/urls/job_template.py +++ b/awx/api/urls/job_template.py @@ -15,6 +15,7 @@ from awx.api.views import ( JobTemplateActivityStreamList, JobTemplateNotificationTemplatesAnyList, JobTemplateNotificationTemplatesErrorList, + JobTemplateNotificationTemplatesStartedList, JobTemplateNotificationTemplatesSuccessList, JobTemplateInstanceGroupsList, JobTemplateAccessList, @@ -36,6 +37,8 @@ urls = [ url(r'^(?P[0-9]+)/activity_stream/$', JobTemplateActivityStreamList.as_view(), name='job_template_activity_stream_list'), url(r'^(?P[0-9]+)/notification_templates_any/$', JobTemplateNotificationTemplatesAnyList.as_view(), name='job_template_notification_templates_any_list'), + url(r'^(?P[0-9]+)/notification_templates_started/$', JobTemplateNotificationTemplatesStartedList.as_view(), + name='job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', JobTemplateNotificationTemplatesErrorList.as_view(), name='job_template_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', JobTemplateNotificationTemplatesSuccessList.as_view(), diff --git a/awx/api/urls/organization.py b/awx/api/urls/organization.py index 911143bb86..af04f1fc77 100644 --- a/awx/api/urls/organization.py +++ b/awx/api/urls/organization.py @@ -17,6 +17,7 @@ from awx.api.views import ( OrganizationNotificationTemplatesList, OrganizationNotificationTemplatesAnyList, OrganizationNotificationTemplatesErrorList, + OrganizationNotificationTemplatesStartedList, OrganizationNotificationTemplatesSuccessList, OrganizationInstanceGroupsList, OrganizationObjectRolesList, @@ -25,7 +26,7 @@ from awx.api.views import ( ) -urls = [ +urls = [ url(r'^$', OrganizationList.as_view(), name='organization_list'), url(r'^(?P[0-9]+)/$', OrganizationDetail.as_view(), name='organization_detail'), url(r'^(?P[0-9]+)/users/$', OrganizationUsersList.as_view(), name='organization_users_list'), @@ -39,6 +40,8 @@ urls = [ url(r'^(?P[0-9]+)/notification_templates/$', OrganizationNotificationTemplatesList.as_view(), name='organization_notification_templates_list'), url(r'^(?P[0-9]+)/notification_templates_any/$', OrganizationNotificationTemplatesAnyList.as_view(), name='organization_notification_templates_any_list'), + url(r'^(?P[0-9]+)/notification_templates_started/$', OrganizationNotificationTemplatesStartedList.as_view(), + name='organization_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', OrganizationNotificationTemplatesErrorList.as_view(), name='organization_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', OrganizationNotificationTemplatesSuccessList.as_view(), diff --git a/awx/api/urls/project.py b/awx/api/urls/project.py index 263014e6e2..a37bf0cefb 100644 --- a/awx/api/urls/project.py +++ b/awx/api/urls/project.py @@ -16,6 +16,7 @@ from awx.api.views import ( ProjectSchedulesList, ProjectNotificationTemplatesAnyList, ProjectNotificationTemplatesErrorList, + ProjectNotificationTemplatesStartedList, ProjectNotificationTemplatesSuccessList, ProjectObjectRolesList, ProjectAccessList, @@ -38,6 +39,8 @@ urls = [ url(r'^(?P[0-9]+)/notification_templates_error/$', ProjectNotificationTemplatesErrorList.as_view(), name='project_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', ProjectNotificationTemplatesSuccessList.as_view(), name='project_notification_templates_success_list'), + url(r'^(?P[0-9]+)/notification_templates_started/$', ProjectNotificationTemplatesStartedList.as_view(), + name='project_notification_templates_started_list'), url(r'^(?P[0-9]+)/object_roles/$', ProjectObjectRolesList.as_view(), name='project_object_roles_list'), url(r'^(?P[0-9]+)/access_list/$', ProjectAccessList.as_view(), name='project_access_list'), url(r'^(?P[0-9]+)/copy/$', ProjectCopy.as_view(), name='project_copy'), diff --git a/awx/api/urls/system_job_template.py b/awx/api/urls/system_job_template.py index 637ce05060..294ed863b5 100644 --- a/awx/api/urls/system_job_template.py +++ b/awx/api/urls/system_job_template.py @@ -11,6 +11,7 @@ from awx.api.views import ( SystemJobTemplateSchedulesList, SystemJobTemplateNotificationTemplatesAnyList, SystemJobTemplateNotificationTemplatesErrorList, + SystemJobTemplateNotificationTemplatesStartedList, SystemJobTemplateNotificationTemplatesSuccessList, ) @@ -23,6 +24,8 @@ urls = [ url(r'^(?P[0-9]+)/schedules/$', SystemJobTemplateSchedulesList.as_view(), name='system_job_template_schedules_list'), url(r'^(?P[0-9]+)/notification_templates_any/$', SystemJobTemplateNotificationTemplatesAnyList.as_view(), name='system_job_template_notification_templates_any_list'), + url(r'^(?P[0-9]+)/notification_templates_any/$', SystemJobTemplateNotificationTemplatesStartedList.as_view(), + name='system_job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', SystemJobTemplateNotificationTemplatesErrorList.as_view(), name='system_job_template_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', SystemJobTemplateNotificationTemplatesSuccessList.as_view(), diff --git a/awx/api/urls/workflow_job_template.py b/awx/api/urls/workflow_job_template.py index 2c6f880ce5..67ac690b60 100644 --- a/awx/api/urls/workflow_job_template.py +++ b/awx/api/urls/workflow_job_template.py @@ -15,6 +15,7 @@ from awx.api.views import ( WorkflowJobTemplateActivityStreamList, WorkflowJobTemplateNotificationTemplatesAnyList, WorkflowJobTemplateNotificationTemplatesErrorList, + WorkflowJobTemplateNotificationTemplatesStartedList, WorkflowJobTemplateNotificationTemplatesSuccessList, WorkflowJobTemplateAccessList, WorkflowJobTemplateObjectRolesList, @@ -34,6 +35,8 @@ urls = [ url(r'^(?P[0-9]+)/activity_stream/$', WorkflowJobTemplateActivityStreamList.as_view(), name='workflow_job_template_activity_stream_list'), url(r'^(?P[0-9]+)/notification_templates_any/$', WorkflowJobTemplateNotificationTemplatesAnyList.as_view(), name='workflow_job_template_notification_templates_any_list'), + url(r'^(?P[0-9]+)/notification_templates_started/$', WorkflowJobTemplateNotificationTemplatesStartedList.as_view(), + name='workflow_job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', WorkflowJobTemplateNotificationTemplatesErrorList.as_view(), name='workflow_job_template_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', WorkflowJobTemplateNotificationTemplatesSuccessList.as_view(), diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 9f85854cdd..92eaa04598 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -116,6 +116,7 @@ from awx.api.views.organization import ( # noqa OrganizationNotificationTemplatesList, OrganizationNotificationTemplatesAnyList, OrganizationNotificationTemplatesErrorList, + OrganizationNotificationTemplatesStartedList, OrganizationNotificationTemplatesSuccessList, OrganizationInstanceGroupsList, OrganizationAccessList, @@ -750,6 +751,14 @@ class ProjectNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): relationship = 'notification_templates_any' +class ProjectNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): + + model = models.NotificationTemplate + serializer_class = serializers.NotificationTemplateSerializer + parent_model = models.Project + relationship = 'notification_templates_started' + + class ProjectNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): model = models.NotificationTemplate @@ -2113,6 +2122,11 @@ class InventorySourceNotificationTemplatesAnyList(SubListCreateAttachDetachAPIVi return super(InventorySourceNotificationTemplatesAnyList, self).post(request, *args, **kwargs) +class InventorySourceNotificationTemplatesStartedList(InventorySourceNotificationTemplatesAnyList): + + relationship = 'notification_templates_started' + + class InventorySourceNotificationTemplatesErrorList(InventorySourceNotificationTemplatesAnyList): relationship = 'notification_templates_error' @@ -2629,6 +2643,14 @@ class JobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): relationship = 'notification_templates_any' +class JobTemplateNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): + + model = models.NotificationTemplate + serializer_class = serializers.NotificationTemplateSerializer + parent_model = models.JobTemplate + relationship = 'notification_templates_started' + + class JobTemplateNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): model = models.NotificationTemplate @@ -3237,6 +3259,14 @@ class WorkflowJobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachA relationship = 'notification_templates_any' +class WorkflowJobTemplateNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): + + model = models.NotificationTemplate + serializer_class = serializers.NotificationTemplateSerializer + parent_model = models.WorkflowJobTemplate + relationship = 'notification_templates_started' + + class WorkflowJobTemplateNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): model = models.NotificationTemplate @@ -3414,6 +3444,14 @@ class SystemJobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachAPI relationship = 'notification_templates_any' +class SystemJobTemplateNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): + + model = models.NotificationTemplate + serializer_class = serializers.NotificationTemplateSerializer + parent_model = models.SystemJobTemplate + relationship = 'notification_templates_started' + + class SystemJobTemplateNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): model = models.NotificationTemplate diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py index 37cce7d289..c86306e9cd 100644 --- a/awx/api/views/organization.py +++ b/awx/api/views/organization.py @@ -181,6 +181,14 @@ class OrganizationNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView) relationship = 'notification_templates_any' +class OrganizationNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): + + model = NotificationTemplate + serializer_class = NotificationTemplateSerializer + parent_model = Organization + relationship = 'notification_templates_started' + + class OrganizationNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): model = NotificationTemplate diff --git a/awx/main/models/ad_hoc_commands.py b/awx/main/models/ad_hoc_commands.py index 5671bb3383..c843b9f577 100644 --- a/awx/main/models/ad_hoc_commands.py +++ b/awx/main/models/ad_hoc_commands.py @@ -170,11 +170,14 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin): active_templates['error'].add(templ) for templ in base_notification_templates.filter(organization_notification_templates_for_success=org): active_templates['success'].add(templ) + for templ in base_notification_templates.filter(organization_notification_templates_for_started=org): + active_templates['started'].add(templ) for templ in base_notification_templates.filter(organization_notification_templates_for_any=org): active_templates['any'].add(templ) active_templates['error'] = list(active_templates['error']) active_templates['any'] = list(active_templates['any']) active_templates['success'] = list(active_templates['success']) + active_templates['started'] = list(active_templates['started']) return active_templates def get_passwords_needed_to_start(self): diff --git a/awx/main/models/base.py b/awx/main/models/base.py index ba44f9aa98..ec36ec8785 100644 --- a/awx/main/models/base.py +++ b/awx/main/models/base.py @@ -386,6 +386,12 @@ class NotificationFieldsModel(BaseModel): related_name='%(class)s_notification_templates_for_success' ) + notification_templates_started = models.ManyToManyField( + "NotificationTemplate", + blank=True, + related_name='%(class)s_notification_templates_for_started' + ) + notification_templates_any = models.ManyToManyField( "NotificationTemplate", blank=True, diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index e7244bac5b..9301c63c1d 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -1619,6 +1619,8 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, CustomVirtualE base_notification_templates = NotificationTemplate.objects error_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_errors__in=[self])) + started_notification_templates = list(base_notification_templates + .filter(unifiedjobtemplate_notification_templates_for_started__in=[self])) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success__in=[self])) any_notification_templates = list(base_notification_templates @@ -1626,11 +1628,14 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, CustomVirtualE if self.inventory.organization is not None: error_notification_templates = set(error_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_errors=self.inventory.organization))) + started_notification_templates = set(started_notification_templates + list(base_notification_templates + .filter(organization_notification_templates_for_started=self.inventory.organization))) success_notification_templates = set(success_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_success=self.inventory.organization))) any_notification_templates = set(any_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_any=self.inventory.organization))) return dict(error=list(error_notification_templates), + started=list(started_notification_templates), success=list(success_notification_templates), any=list(any_notification_templates)) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index b8f2661c97..f20d074ead 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -435,6 +435,8 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour base_notification_templates = NotificationTemplate.objects error_notification_templates = list(base_notification_templates.filter( unifiedjobtemplate_notification_templates_for_errors__in=[self, self.project])) + started_notification_templates = list(base_notification_templates.filter( + unifiedjobtemplate_notification_templates_for_started__in=[self, self.project])) success_notification_templates = list(base_notification_templates.filter( unifiedjobtemplate_notification_templates_for_success__in=[self, self.project])) any_notification_templates = list(base_notification_templates.filter( @@ -443,11 +445,16 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour if self.project is not None and self.project.organization is not None: error_notification_templates = set(error_notification_templates + list(base_notification_templates.filter( organization_notification_templates_for_errors=self.project.organization))) + started_notification_templates = set(started_notification_templates + list(base_notification_templates.filter( + organization_notification_templates_for_started=self.project.organization))) success_notification_templates = set(success_notification_templates + list(base_notification_templates.filter( organization_notification_templates_for_success=self.project.organization))) any_notification_templates = set(any_notification_templates + list(base_notification_templates.filter( organization_notification_templates_for_any=self.project.organization))) - return dict(error=list(error_notification_templates), success=list(success_notification_templates), any=list(any_notification_templates)) + return dict(error=list(error_notification_templates), + started=list(started_notification_templates), + success=list(success_notification_templates), + any=list(any_notification_templates)) ''' RelatedJobsMixin @@ -1133,11 +1140,14 @@ class SystemJobTemplate(UnifiedJobTemplate, SystemJobOptions): base_notification_templates = NotificationTemplate.objects.all() error_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_errors__in=[self])) + started_notification_templates = list(base_notification_templates + .filter(unifiedjobtemplate_notification_templates_for_started__in=[self])) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success__in=[self])) any_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_any__in=[self])) return dict(error=list(error_notification_templates), + started=list(started_notification_templates), success=list(success_notification_templates), any=list(any_notification_templates)) diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index 9fa6e31b17..0b036d3783 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -129,7 +129,7 @@ class NotificationTemplate(CommonModelNameNotUnique): if field not in notification_configuration: if 'default' in params: notification_configuration[field] = params['default'] - backend_obj = self.notification_class(**notification_configuration) + backend_obj = self.notification_class(**notification_configuration) notification_obj = EmailMessage(subject, backend_obj.format_body(body), sender, recipients) with set_environ(**settings.AWX_TASK_ENV): return backend_obj.send_messages([notification_obj]) @@ -221,10 +221,13 @@ class JobNotificationMixin(object): def build_notification_failed_message(self): return self._build_notification_message('failed') + def build_notification_running_message(self): + return self._build_notification_message('running') + def send_notification_templates(self, status_str): from awx.main.tasks import send_notifications # avoid circular import - if status_str not in ['succeeded', 'failed']: - raise ValueError(_("status_str must be either succeeded or failed")) + if status_str not in ['succeeded', 'failed', 'running']: + raise ValueError(_("status_str must be either running, succeeded or failed")) try: notification_templates = self.get_notification_templates() except Exception: @@ -233,6 +236,8 @@ class JobNotificationMixin(object): if notification_templates: if status_str == 'succeeded': notification_template_type = 'success' + elif status_str == 'running': + notification_template_type = 'started' else: notification_template_type = 'error' all_notification_templates = set(notification_templates.get(notification_template_type, []) + notification_templates.get('any', [])) diff --git a/awx/main/models/projects.py b/awx/main/models/projects.py index 1759375b51..9da401a909 100644 --- a/awx/main/models/projects.py +++ b/awx/main/models/projects.py @@ -411,6 +411,8 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn base_notification_templates = NotificationTemplate.objects error_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_errors=self)) + started_notification_templates = list(base_notification_templates + .filter(unifiedjobtemplate_notification_templates_for_started=self)) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success=self)) any_notification_templates = list(base_notification_templates @@ -420,6 +422,9 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn error_notification_templates = set(error_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_errors=self.organization))) + started_notification_templates = set(started_notification_templates + + list(base_notification_templates + .filter(organization_notification_templates_for_started=self.organization))) success_notification_templates = set(success_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_success=self.organization))) @@ -427,6 +432,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn list(base_notification_templates .filter(organization_notification_templates_for_any=self.organization))) return dict(error=list(error_notification_templates), + started=list(started_notification_templates), success=list(success_notification_templates), any=list(any_notification_templates)) @@ -567,5 +573,3 @@ class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin, TaskManage if not selected_groups: return self.global_instance_groups return selected_groups - - diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py index 4e4f2a8dbb..3be0938058 100644 --- a/awx/main/models/workflow.py +++ b/awx/main/models/workflow.py @@ -419,11 +419,14 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl base_notification_templates = NotificationTemplate.objects.all() error_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_errors__in=[self])) + started_notification_templates = list(base_notification_templates + .filter(unifiedjobtemplate_notification_templates_for_started__in=[self])) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success__in=[self])) any_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_any__in=[self])) return dict(error=list(error_notification_templates), + started=list(started_notification_templates), success=list(success_notification_templates), any=list(any_notification_templates)) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index e0d595559f..46181b8b02 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -587,4 +587,4 @@ class TaskManager(): # Operations whose queries rely on modifications made during the atomic scheduling session for wfj in WorkflowJob.objects.filter(id__in=finished_wfjs): - wfj.send_notification_templates('succeeded' if wfj.status == 'successful' else 'failed') + wfj.send_notification_templates('started' if wfj.status == 'running' else ('succeeded' if wfj.status == 'successful' else 'failed')) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index f899fadb05..7aba377381 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -314,7 +314,7 @@ def send_notifications(notification_list, job_id=None): update_fields = ['status', 'notifications_sent'] try: sent = notification.notification_template.send(notification.subject, notification.body) - notification.status = "successful" + notification.status = "successful" or "running" notification.notifications_sent = sent except Exception as e: logger.error("Send Notification Failed {}".format(e)) @@ -1115,6 +1115,8 @@ class BaseTask(object): self.instance = self.update_model(pk, status='running', start_args='') # blank field to remove encrypted passwords + self.instance.send_notification_templates("running") + self.instance.websocket_emit_status("running") status, rc = 'error', None extra_update_fields = {} From 98fa1fc813b9b6a9b8a3b3b6b08a276b0a0020ee Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 31 May 2019 15:12:21 -0400 Subject: [PATCH 02/15] Add migration file --- .../migrations/0081_v360_notify_on_start.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 awx/main/migrations/0081_v360_notify_on_start.py diff --git a/awx/main/migrations/0081_v360_notify_on_start.py b/awx/main/migrations/0081_v360_notify_on_start.py new file mode 100644 index 0000000000..7fc783d20d --- /dev/null +++ b/awx/main/migrations/0081_v360_notify_on_start.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-05-30 20:35 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0080_v360_replace_job_origin'), + ] + + operations = [ + migrations.AddField( + model_name='organization', + name='notification_templates_started', + field=models.ManyToManyField(blank=True, related_name='organization_notification_templates_for_started', to='main.NotificationTemplate'), + ), + migrations.AddField( + model_name='unifiedjobtemplate', + name='notification_templates_started', + field=models.ManyToManyField(blank=True, related_name='unifiedjobtemplate_notification_templates_for_started', to='main.NotificationTemplate'), + ), + ] From 8d6e1f09270faad983c0918b9c44c490407e7eef Mon Sep 17 00:00:00 2001 From: beeankha Date: Mon, 3 Jun 2019 20:57:08 -0400 Subject: [PATCH 03/15] Trigger running notifications in WFJs and edit unit test --- awx/api/views/__init__.py | 60 ++++++------------------------ awx/main/models/notifications.py | 17 ++++++--- awx/main/scheduler/task_manager.py | 3 +- awx/main/tasks.py | 5 +-- awx/main/tests/unit/test_tasks.py | 2 + 5 files changed, 29 insertions(+), 58 deletions(-) diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 92eaa04598..b494986ac8 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -751,27 +751,18 @@ class ProjectNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): relationship = 'notification_templates_any' -class ProjectNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): +class ProjectNotificationTemplatesStartedList(ProjectNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.Project relationship = 'notification_templates_started' -class ProjectNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): +class ProjectNotificationTemplatesErrorList(ProjectNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.Project relationship = 'notification_templates_error' -class ProjectNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): +class ProjectNotificationTemplatesSuccessList(ProjectNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.Project relationship = 'notification_templates_success' @@ -2643,27 +2634,18 @@ class JobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): relationship = 'notification_templates_any' -class JobTemplateNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): +class JobTemplateNotificationTemplatesStartedList(JobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.JobTemplate relationship = 'notification_templates_started' -class JobTemplateNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): +class JobTemplateNotificationTemplatesErrorList(JobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.JobTemplate relationship = 'notification_templates_error' -class JobTemplateNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): +class JobTemplateNotificationTemplatesSuccessList(JobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.JobTemplate relationship = 'notification_templates_success' @@ -3259,27 +3241,18 @@ class WorkflowJobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachA relationship = 'notification_templates_any' -class WorkflowJobTemplateNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): +class WorkflowJobTemplateNotificationTemplatesStartedList(WorkflowJobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.WorkflowJobTemplate relationship = 'notification_templates_started' -class WorkflowJobTemplateNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): +class WorkflowJobTemplateNotificationTemplatesErrorList(WorkflowJobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.WorkflowJobTemplate relationship = 'notification_templates_error' -class WorkflowJobTemplateNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): +class WorkflowJobTemplateNotificationTemplatesSuccessList(WorkflowJobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.WorkflowJobTemplate relationship = 'notification_templates_success' @@ -3444,27 +3417,18 @@ class SystemJobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachAPI relationship = 'notification_templates_any' -class SystemJobTemplateNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): +class SystemJobTemplateNotificationTemplatesStartedList(SystemJobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.SystemJobTemplate relationship = 'notification_templates_started' -class SystemJobTemplateNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): +class SystemJobTemplateNotificationTemplatesErrorList(SystemJobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.SystemJobTemplate relationship = 'notification_templates_error' -class SystemJobTemplateNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): +class SystemJobTemplateNotificationTemplatesSuccessList(SystemJobTemplateNotificationTemplatesAnyList): - model = models.NotificationTemplate - serializer_class = serializers.NotificationTemplateSerializer - parent_model = models.SystemJobTemplate relationship = 'notification_templates_success' diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index 0b036d3783..8f16cf4af8 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -7,6 +7,7 @@ import logging from django.db import models from django.conf import settings from django.core.mail.message import EmailMessage +from django.db import connection from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_str, force_text @@ -240,12 +241,16 @@ class JobNotificationMixin(object): notification_template_type = 'started' else: notification_template_type = 'error' - all_notification_templates = set(notification_templates.get(notification_template_type, []) + notification_templates.get('any', [])) - if len(all_notification_templates): - try: - (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() - except AttributeError: - raise NotImplementedError("build_notification_%s_message() does not exist" % status_str) + all_notification_templates = set(notification_templates.get(notification_template_type, [])) + if status_str != 'running': + all_notification_templates.update(notification_templates.get('any', [])) + try: + (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() + except AttributeError: + raise NotImplementedError("build_notification_%s_message() does not exist" % status_str) + + def send_it(): send_notifications.delay([n.generate_notification(notification_subject, notification_body).id for n in all_notification_templates], job_id=self.id) + connection.on_commit(send_it) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 46181b8b02..75c0544ff6 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -233,6 +233,7 @@ class TaskManager(): else: if type(task) is WorkflowJob: task.status = 'running' + task.send_notification_templates('running') # <---- logger.debug('Transitioning %s to running status.', task.log_format) schedule_task_manager() elif not task.supports_isolation() and rampart_group.controller_id: @@ -587,4 +588,4 @@ class TaskManager(): # Operations whose queries rely on modifications made during the atomic scheduling session for wfj in WorkflowJob.objects.filter(id__in=finished_wfjs): - wfj.send_notification_templates('started' if wfj.status == 'running' else ('succeeded' if wfj.status == 'successful' else 'failed')) + wfj.send_notification_templates('succeeded' if wfj.status == 'successful' else 'failed') diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 7aba377381..d73737a4b3 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -314,7 +314,7 @@ def send_notifications(notification_list, job_id=None): update_fields = ['status', 'notifications_sent'] try: sent = notification.notification_template.send(notification.subject, notification.body) - notification.status = "successful" or "running" + notification.status = "successful" notification.notifications_sent = sent except Exception as e: logger.error("Send Notification Failed {}".format(e)) @@ -1115,8 +1115,6 @@ class BaseTask(object): self.instance = self.update_model(pk, status='running', start_args='') # blank field to remove encrypted passwords - self.instance.send_notification_templates("running") - self.instance.websocket_emit_status("running") status, rc = 'error', None extra_update_fields = {} @@ -1133,6 +1131,7 @@ class BaseTask(object): try: isolated = self.instance.is_isolated() + self.instance.send_notification_templates("running") self.pre_run_hook(self.instance) if self.instance.cancel_flag: self.instance = self.update_model(self.instance.pk, status='canceled') diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 2915a65aa1..0a84f754a5 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -378,6 +378,7 @@ class TestGenericRun(): job.status = 'running' job.cancel_flag = True job.websocket_emit_status = mock.Mock() + job.send_notification_templates = mock.Mock() task = tasks.RunJob() task.update_model = mock.Mock(wraps=update_model_wrapper) @@ -536,6 +537,7 @@ class TestAdhocRun(TestJobExecution): def test_options_jinja_usage(self, adhoc_job, adhoc_update_model_wrapper): adhoc_job.module_args = '{{ ansible_ssh_pass }}' adhoc_job.websocket_emit_status = mock.Mock() + adhoc_job.send_notification_templates = mock.Mock() task = tasks.RunAdHocCommand() task.update_model = mock.Mock(wraps=adhoc_update_model_wrapper) From dd372548a910eeae3b926bbd5f23ed5b6edb3907 Mon Sep 17 00:00:00 2001 From: beeankha Date: Mon, 3 Jun 2019 21:20:41 -0400 Subject: [PATCH 04/15] Update swagger test --- awx/main/scheduler/task_manager.py | 2 +- awx/main/tests/docs/test_swagger_generation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 75c0544ff6..2dec7a985e 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -233,7 +233,7 @@ class TaskManager(): else: if type(task) is WorkflowJob: task.status = 'running' - task.send_notification_templates('running') # <---- + task.send_notification_templates('running') logger.debug('Transitioning %s to running status.', task.log_format) schedule_task_manager() elif not task.supports_isolation() and rampart_group.controller_id: diff --git a/awx/main/tests/docs/test_swagger_generation.py b/awx/main/tests/docs/test_swagger_generation.py index 9f00193515..5ee30e0b1e 100644 --- a/awx/main/tests/docs/test_swagger_generation.py +++ b/awx/main/tests/docs/test_swagger_generation.py @@ -91,7 +91,7 @@ class TestSwaggerGeneration(): # The number of API endpoints changes over time, but let's just check # for a reasonable number here; if this test starts failing, raise/lower the bounds paths = JSON['paths'] - assert 250 < len(paths) < 300 + assert 250 < len(paths) < 350 assert list(paths['/api/'].keys()) == ['get'] assert list(paths['/api/v2/'].keys()) == ['get'] assert list(sorted( From 68fe23d8b735c5316477927ac5e13676c7d2529b Mon Sep 17 00:00:00 2001 From: beeankha Date: Tue, 4 Jun 2019 13:08:22 -0400 Subject: [PATCH 05/15] Update Organization Notification Template subclass, move success/fail wfj notification trigger --- awx/api/views/organization.py | 15 +++------------ awx/main/scheduler/task_manager.py | 9 +++------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py index c86306e9cd..de5ef998cd 100644 --- a/awx/api/views/organization.py +++ b/awx/api/views/organization.py @@ -181,27 +181,18 @@ class OrganizationNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView) relationship = 'notification_templates_any' -class OrganizationNotificationTemplatesStartedList(SubListCreateAttachDetachAPIView): +class OrganizationNotificationTemplatesStartedList(OrganizationNotificationTemplatesAnyList): - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization relationship = 'notification_templates_started' -class OrganizationNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): +class OrganizationNotificationTemplatesErrorList(OrganizationNotificationTemplatesAnyList): - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization relationship = 'notification_templates_error' -class OrganizationNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): +class OrganizationNotificationTemplatesSuccessList(OrganizationNotificationTemplatesAnyList): - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization relationship = 'notification_templates_success' diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 2dec7a985e..5532bef89b 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -193,6 +193,8 @@ class TaskManager(): status_changed = True if status_changed: workflow_job.websocket_emit_status(workflow_job.status) + # Operations whose queries rely on modifications made during the atomic scheduling session + connection.on_commit(lambda: workflow_job.send_notification_templates('succeeded' if workflow_job.status == 'successful' else 'failed')) if workflow_job.spawned_by_workflow: schedule_task_manager() return result @@ -582,10 +584,5 @@ class TaskManager(): logger.debug("Not running scheduler, another task holds lock") return logger.debug("Starting Scheduler") - with task_manager_bulk_reschedule(): - finished_wfjs = self._schedule() - - # Operations whose queries rely on modifications made during the atomic scheduling session - for wfj in WorkflowJob.objects.filter(id__in=finished_wfjs): - wfj.send_notification_templates('succeeded' if wfj.status == 'successful' else 'failed') + self._schedule() From 95896b1acda0e6045fb2a269a181e7cb13556f6b Mon Sep 17 00:00:00 2001 From: beeankha Date: Tue, 4 Jun 2019 15:31:57 -0400 Subject: [PATCH 06/15] Edit wfj running notification trigger --- awx/main/scheduler/task_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 5532bef89b..6c25fbef4a 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -235,7 +235,7 @@ class TaskManager(): else: if type(task) is WorkflowJob: task.status = 'running' - task.send_notification_templates('running') + connection.on_commit(lambda: task.send_notification_templates('running')) logger.debug('Transitioning %s to running status.', task.log_format) schedule_task_manager() elif not task.supports_isolation() and rampart_group.controller_id: From 9cfed6f2a85ac66d2dc83a22a40e6776ae359333 Mon Sep 17 00:00:00 2001 From: beeankha Date: Wed, 5 Jun 2019 10:18:33 -0400 Subject: [PATCH 07/15] Add check for no-op case back, remove redundant on_commit code --- awx/main/models/notifications.py | 23 ++++++++++++----------- awx/main/scheduler/task_manager.py | 4 ++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index 8f16cf4af8..222127f1d6 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -242,15 +242,16 @@ class JobNotificationMixin(object): else: notification_template_type = 'error' all_notification_templates = set(notification_templates.get(notification_template_type, [])) - if status_str != 'running': - all_notification_templates.update(notification_templates.get('any', [])) - try: - (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() - except AttributeError: - raise NotImplementedError("build_notification_%s_message() does not exist" % status_str) + if len(all_notification_templates): + if status_str != 'running': + all_notification_templates.update(notification_templates.get('any', [])) + try: + (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() + except AttributeError: + raise NotImplementedError("build_notification_%s_message() does not exist" % status_str) - def send_it(): - send_notifications.delay([n.generate_notification(notification_subject, notification_body).id - for n in all_notification_templates], - job_id=self.id) - connection.on_commit(send_it) + def send_it(): + send_notifications.delay([n.generate_notification(notification_subject, notification_body).id + for n in all_notification_templates], + job_id=self.id) + connection.on_commit(send_it) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 6c25fbef4a..b79abbad0d 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -194,7 +194,7 @@ class TaskManager(): if status_changed: workflow_job.websocket_emit_status(workflow_job.status) # Operations whose queries rely on modifications made during the atomic scheduling session - connection.on_commit(lambda: workflow_job.send_notification_templates('succeeded' if workflow_job.status == 'successful' else 'failed')) + workflow_job.send_notification_templates('succeeded' if workflow_job.status == 'successful' else 'failed') if workflow_job.spawned_by_workflow: schedule_task_manager() return result @@ -235,7 +235,7 @@ class TaskManager(): else: if type(task) is WorkflowJob: task.status = 'running' - connection.on_commit(lambda: task.send_notification_templates('running')) + task.send_notification_templates('running') logger.debug('Transitioning %s to running status.', task.log_format) schedule_task_manager() elif not task.supports_isolation() and rampart_group.controller_id: From 7687eddf6dea23c15c1bc22b15dbeb5f0daa34c5 Mon Sep 17 00:00:00 2001 From: beeankha Date: Wed, 5 Jun 2019 15:58:05 -0400 Subject: [PATCH 08/15] Add api test, edit AWX docs --- .../functional/api/test_notifications.py | 24 +++++++++++++++++++ docs/notification_system.md | 6 +++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 awx/main/tests/functional/api/test_notifications.py diff --git a/awx/main/tests/functional/api/test_notifications.py b/awx/main/tests/functional/api/test_notifications.py new file mode 100644 index 0000000000..d3cd3ca637 --- /dev/null +++ b/awx/main/tests/functional/api/test_notifications.py @@ -0,0 +1,24 @@ +import pytest + +from awx.api.versioning import reverse + + +@pytest.mark.django_db +def test_get_jt_running_notification(get, admin, job_template): + url = reverse('api:job_template_notification_templates_started_list', kwargs={'pk': job_template.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 0 + + +@pytest.mark.django_db +def test_post_jt_running_notification(get, post, admin, notification_template, job_template): + url = reverse('api:job_template_notification_templates_started_list', kwargs={'pk': job_template.pk}) + response = post(url, + dict(id=notification_template.id, + associate=True), + admin) + assert response.status_code == 204 + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 diff --git a/docs/notification_system.md b/docs/notification_system.md index 1415f30c96..6b751beca3 100644 --- a/docs/notification_system.md +++ b/docs/notification_system.md @@ -22,9 +22,11 @@ Notification templates assigned at certain levels will inherit notifications def ## Workflow -When a job succeeds or fails, the error or success handler will pull a list of relevant notifications using the procedure defined above. It will then create a Notification object for each one containing relevant details about the job and then **sends** it to the destination (email addresses, slack channel(s), SMS numbers, etc.). These Notification objects are available as related resources on job types (Jobs, Inventory Updates, Project Updates), and also at `/api/v2/notifications`. You may also see what notifications have been sent from a notifications by examining its related resources. +When a job starts, succeeds or fails, the running, error or success handler will pull a list of relevant notifications using the procedure defined above. It will then create a Notification object for each one containing relevant details about the job and then **sends** it to the destination (email addresses, slack channel(s), SMS numbers, etc.). These Notification objects are available as related resources on job types (Jobs, Inventory Updates, Project Updates), and also at `/api/v2/notifications`. You may also see what notifications have been sent from a notifications by examining its related resources. -Notifications can succeed or fail but that will not cause its associated job to succeed or fail. The status of the notification can be viewed at its detail endpoint: `/api/v2/notifications/` +When a notification is associated to a job via the `/api/v2/.../notification_templates_any/` endpoint, it will send upon success OR fail, but _not_ on start. + +Notifications can succeed or fail but that will _not_ cause its associated job to succeed or fail. The status of the notification can be viewed at its detail endpoint: `/api/v2/notifications/` ## Testing Notifications Before Using Them From 30741e762a088d21ab13256882e7c0c499aa2aff Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 6 Jun 2019 15:26:00 -0400 Subject: [PATCH 09/15] Add more notification tests --- .../functional/api/test_notifications.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/awx/main/tests/functional/api/test_notifications.py b/awx/main/tests/functional/api/test_notifications.py index d3cd3ca637..d211026b07 100644 --- a/awx/main/tests/functional/api/test_notifications.py +++ b/awx/main/tests/functional/api/test_notifications.py @@ -3,6 +3,69 @@ import pytest from awx.api.versioning import reverse +@pytest.mark.django_db +def test_get_org_running_notification(get, admin, organization): + url = reverse('api:organization_notification_templates_started_list', kwargs={'pk': organization.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 0 + + +@pytest.mark.django_db +def test_post_org_running_notification(get, post, admin, notification_template, organization): + url = reverse('api:organization_notification_templates_started_list', kwargs={'pk': organization.pk}) + response = post(url, + dict(id=notification_template.id, + associate=True), + admin) + assert response.status_code == 204 + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 + + +@pytest.mark.django_db +def test_get_project_running_notification(get, admin, project): + url = reverse('api:project_notification_templates_started_list', kwargs={'pk': project.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 0 + + +@pytest.mark.django_db +def test_post_project_running_notification(get, post, admin, notification_template, project): + url = reverse('api:project_notification_templates_started_list', kwargs={'pk': project.pk}) + response = post(url, + dict(id=notification_template.id, + associate=True), + admin) + assert response.status_code == 204 + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 + + +@pytest.mark.django_db +def test_get_inv_src_running_notification(get, admin, inventory_source): + url = reverse('api:inventory_source_notification_templates_started_list', kwargs={'pk': inventory_source.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 0 + + +@pytest.mark.django_db +def test_post_inv_src_running_notification(get, post, admin, notification_template, inventory_source): + url = reverse('api:inventory_source_notification_templates_started_list', kwargs={'pk': inventory_source.pk}) + response = post(url, + dict(id=notification_template.id, + associate=True), + admin) + assert response.status_code == 204 + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 + + @pytest.mark.django_db def test_get_jt_running_notification(get, admin, job_template): url = reverse('api:job_template_notification_templates_started_list', kwargs={'pk': job_template.pk}) @@ -22,3 +85,45 @@ def test_post_jt_running_notification(get, post, admin, notification_template, j response = get(url, admin) assert response.status_code == 200 assert len(response.data['results']) == 1 + + +@pytest.mark.django_db +def test_get_sys_jt_running_notification(get, admin, system_job_template): + url = reverse('api:system_job_template_notification_templates_started_list', kwargs={'pk': system_job_template.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 0 + + +@pytest.mark.django_db +def test_post_sys_jt_running_notification(get, post, admin, notification_template, system_job_template): + url = reverse('api:system_job_template_notification_templates_started_list', kwargs={'pk': system_job_template.pk}) + response = post(url, + dict(id=notification_template.id, + associate=True), + admin) + assert response.status_code == 204 + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 + + +@pytest.mark.django_db +def test_get_wfjt_running_notification(get, admin, workflow_job_template): + url = reverse('api:workflow_job_template_notification_templates_started_list', kwargs={'pk': workflow_job_template.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 0 + + +@pytest.mark.django_db +def test_post_wfjt_running_notification(get, post, admin, notification_template, workflow_job_template): + url = reverse('api:workflow_job_template_notification_templates_started_list', kwargs={'pk': workflow_job_template.pk}) + response = post(url, + dict(id=notification_template.id, + associate=True), + admin) + assert response.status_code == 204 + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 From d511d63a5a302769269bb31a72a53940886e359d Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 6 Jun 2019 16:13:36 -0400 Subject: [PATCH 10/15] Fixed typo --- awx/api/urls/system_job_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/api/urls/system_job_template.py b/awx/api/urls/system_job_template.py index 294ed863b5..70c0c69836 100644 --- a/awx/api/urls/system_job_template.py +++ b/awx/api/urls/system_job_template.py @@ -24,7 +24,7 @@ urls = [ url(r'^(?P[0-9]+)/schedules/$', SystemJobTemplateSchedulesList.as_view(), name='system_job_template_schedules_list'), url(r'^(?P[0-9]+)/notification_templates_any/$', SystemJobTemplateNotificationTemplatesAnyList.as_view(), name='system_job_template_notification_templates_any_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', SystemJobTemplateNotificationTemplatesStartedList.as_view(), + url(r'^(?P[0-9]+)/notification_templates_started/$', SystemJobTemplateNotificationTemplatesStartedList.as_view(), name='system_job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', SystemJobTemplateNotificationTemplatesErrorList.as_view(), name='system_job_template_notification_templates_error_list'), From c65e6ba30b3c52e65f88e7cd5b0376d3e9811d64 Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 7 Jun 2019 16:33:27 -0400 Subject: [PATCH 11/15] Update the logic for 'any' and 'started' notifications --- awx/main/models/notifications.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index 222127f1d6..c4ba9a7f96 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -235,16 +235,18 @@ class JobNotificationMixin(object): logger.warn("No notification template defined for emitting notification") notification_templates = None if notification_templates: - if status_str == 'succeeded': + all_notification_templates = set() + if status_str != 'running': + all_notification_templates.update(notification_templates.get('any', [])) + notification_template_type = 'any' + elif status_str == 'succeeded': notification_template_type = 'success' elif status_str == 'running': notification_template_type = 'started' else: notification_template_type = 'error' - all_notification_templates = set(notification_templates.get(notification_template_type, [])) + all_notification_templates.update(notification_templates.get(notification_template_type, [])) if len(all_notification_templates): - if status_str != 'running': - all_notification_templates.update(notification_templates.get('any', [])) try: (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() except AttributeError: From c6f1806a230dce9265e86b2f4642eeee486c3b84 Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 13 Jun 2019 10:26:57 -0400 Subject: [PATCH 12/15] Removing references to 'any' state notifications --- awx/api/serializers.py | 6 ----- awx/api/urls/inventory_source.py | 2 -- awx/api/urls/job_template.py | 2 -- awx/api/urls/organization.py | 2 -- awx/api/urls/project.py | 1 - awx/api/urls/system_job_template.py | 2 -- awx/api/urls/workflow_job_template.py | 2 -- awx/api/views/__init__.py | 5 ---- awx/api/views/organization.py | 1 - .../migrations/0081_v360_notify_on_start.py | 9 +++++++ awx/main/models/ad_hoc_commands.py | 5 +--- awx/main/models/base.py | 7 ------ awx/main/models/inventory.py | 7 +----- awx/main/models/jobs.py | 12 ++------- awx/main/models/notifications.py | 10 +++----- awx/main/models/projects.py | 8 +----- awx/main/models/workflow.py | 5 +--- .../tests/functional/api/test_inventory.py | 7 +++--- .../tests/functional/test_notifications.py | 25 ++++++++++--------- .../serializers/test_inventory_serializers.py | 2 +- .../test_job_template_serializers.py | 2 +- docs/notification_system.md | 4 +-- 22 files changed, 39 insertions(+), 87 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 7963c06f5a..381883b81d 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1246,7 +1246,6 @@ class OrganizationSerializer(BaseSerializer): applications = self.reverse('api:organization_applications_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:organization_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates = self.reverse('api:organization_notification_templates_list', kwargs={'pk': obj.pk}), - notification_templates_any = self.reverse('api:organization_notification_templates_any_list', kwargs={'pk': obj.pk}), notification_templates_started = self.reverse('api:organization_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:organization_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:organization_notification_templates_error_list', kwargs={'pk': obj.pk}), @@ -1353,7 +1352,6 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer): scm_inventory_sources = self.reverse('api:project_scm_inventory_sources', kwargs={'pk': obj.pk}), schedules = self.reverse('api:project_schedules_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:project_activity_stream_list', kwargs={'pk': obj.pk}), - notification_templates_any = self.reverse('api:project_notification_templates_any_list', kwargs={'pk': obj.pk}), notification_templates_started = self.reverse('api:project_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:project_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:project_notification_templates_error_list', kwargs={'pk': obj.pk}), @@ -1972,7 +1970,6 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt activity_stream = self.reverse('api:inventory_source_activity_stream_list', kwargs={'pk': obj.pk}), hosts = self.reverse('api:inventory_source_hosts_list', kwargs={'pk': obj.pk}), groups = self.reverse('api:inventory_source_groups_list', kwargs={'pk': obj.pk}), - notification_templates_any = self.reverse('api:inventory_source_notification_templates_any_list', kwargs={'pk': obj.pk}), notification_templates_started = self.reverse('api:inventory_source_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:inventory_source_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:inventory_source_notification_templates_error_list', kwargs={'pk': obj.pk}), @@ -2795,7 +2792,6 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO schedules = self.reverse('api:job_template_schedules_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:job_template_activity_stream_list', kwargs={'pk': obj.pk}), launch = self.reverse('api:job_template_launch', kwargs={'pk': obj.pk}), - notification_templates_any = self.reverse('api:job_template_notification_templates_any_list', kwargs={'pk': obj.pk}), notification_templates_started = self.reverse('api:job_template_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:job_template_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:job_template_notification_templates_error_list', kwargs={'pk': obj.pk}), @@ -3208,7 +3204,6 @@ class SystemJobTemplateSerializer(UnifiedJobTemplateSerializer): jobs = self.reverse('api:system_job_template_jobs_list', kwargs={'pk': obj.pk}), schedules = self.reverse('api:system_job_template_schedules_list', kwargs={'pk': obj.pk}), launch = self.reverse('api:system_job_template_launch', kwargs={'pk': obj.pk}), - notification_templates_any = self.reverse('api:system_job_template_notification_templates_any_list', kwargs={'pk': obj.pk}), notification_templates_started = self.reverse('api:system_job_template_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:system_job_template_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:system_job_template_notification_templates_error_list', kwargs={'pk': obj.pk}), @@ -3276,7 +3271,6 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo workflow_nodes = self.reverse('api:workflow_job_template_workflow_nodes_list', kwargs={'pk': obj.pk}), labels = self.reverse('api:workflow_job_template_label_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:workflow_job_template_activity_stream_list', kwargs={'pk': obj.pk}), - notification_templates_any = self.reverse('api:workflow_job_template_notification_templates_any_list', kwargs={'pk': obj.pk}), notification_templates_started = self.reverse('api:workflow_job_template_notification_templates_started_list', kwargs={'pk': obj.pk}), notification_templates_success = self.reverse('api:workflow_job_template_notification_templates_success_list', kwargs={'pk': obj.pk}), notification_templates_error = self.reverse('api:workflow_job_template_notification_templates_error_list', kwargs={'pk': obj.pk}), diff --git a/awx/api/urls/inventory_source.py b/awx/api/urls/inventory_source.py index e20362b523..98c65d77bc 100644 --- a/awx/api/urls/inventory_source.py +++ b/awx/api/urls/inventory_source.py @@ -30,8 +30,6 @@ urls = [ url(r'^(?P[0-9]+)/credentials/$', InventorySourceCredentialsList.as_view(), name='inventory_source_credentials_list'), url(r'^(?P[0-9]+)/groups/$', InventorySourceGroupsList.as_view(), name='inventory_source_groups_list'), url(r'^(?P[0-9]+)/hosts/$', InventorySourceHostsList.as_view(), name='inventory_source_hosts_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', InventorySourceNotificationTemplatesAnyList.as_view(), - name='inventory_source_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_started/$', InventorySourceNotificationTemplatesStartedList.as_view(), name='inventory_source_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', InventorySourceNotificationTemplatesErrorList.as_view(), diff --git a/awx/api/urls/job_template.py b/awx/api/urls/job_template.py index d9c755e731..3d61094eb4 100644 --- a/awx/api/urls/job_template.py +++ b/awx/api/urls/job_template.py @@ -35,8 +35,6 @@ urls = [ url(r'^(?P[0-9]+)/schedules/$', JobTemplateSchedulesList.as_view(), name='job_template_schedules_list'), url(r'^(?P[0-9]+)/survey_spec/$', JobTemplateSurveySpec.as_view(), name='job_template_survey_spec'), url(r'^(?P[0-9]+)/activity_stream/$', JobTemplateActivityStreamList.as_view(), name='job_template_activity_stream_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', JobTemplateNotificationTemplatesAnyList.as_view(), - name='job_template_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_started/$', JobTemplateNotificationTemplatesStartedList.as_view(), name='job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', JobTemplateNotificationTemplatesErrorList.as_view(), diff --git a/awx/api/urls/organization.py b/awx/api/urls/organization.py index af04f1fc77..2fed9ca97f 100644 --- a/awx/api/urls/organization.py +++ b/awx/api/urls/organization.py @@ -38,8 +38,6 @@ urls = [ url(r'^(?P[0-9]+)/credentials/$', OrganizationCredentialList.as_view(), name='organization_credential_list'), url(r'^(?P[0-9]+)/activity_stream/$', OrganizationActivityStreamList.as_view(), name='organization_activity_stream_list'), url(r'^(?P[0-9]+)/notification_templates/$', OrganizationNotificationTemplatesList.as_view(), name='organization_notification_templates_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', OrganizationNotificationTemplatesAnyList.as_view(), - name='organization_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_started/$', OrganizationNotificationTemplatesStartedList.as_view(), name='organization_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', OrganizationNotificationTemplatesErrorList.as_view(), diff --git a/awx/api/urls/project.py b/awx/api/urls/project.py index a37bf0cefb..192c41f54a 100644 --- a/awx/api/urls/project.py +++ b/awx/api/urls/project.py @@ -35,7 +35,6 @@ urls = [ url(r'^(?P[0-9]+)/project_updates/$', ProjectUpdatesList.as_view(), name='project_updates_list'), url(r'^(?P[0-9]+)/activity_stream/$', ProjectActivityStreamList.as_view(), name='project_activity_stream_list'), url(r'^(?P[0-9]+)/schedules/$', ProjectSchedulesList.as_view(), name='project_schedules_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', ProjectNotificationTemplatesAnyList.as_view(), name='project_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', ProjectNotificationTemplatesErrorList.as_view(), name='project_notification_templates_error_list'), url(r'^(?P[0-9]+)/notification_templates_success/$', ProjectNotificationTemplatesSuccessList.as_view(), name='project_notification_templates_success_list'), diff --git a/awx/api/urls/system_job_template.py b/awx/api/urls/system_job_template.py index 70c0c69836..9886c4c14a 100644 --- a/awx/api/urls/system_job_template.py +++ b/awx/api/urls/system_job_template.py @@ -22,8 +22,6 @@ urls = [ url(r'^(?P[0-9]+)/launch/$', SystemJobTemplateLaunch.as_view(), name='system_job_template_launch'), url(r'^(?P[0-9]+)/jobs/$', SystemJobTemplateJobsList.as_view(), name='system_job_template_jobs_list'), url(r'^(?P[0-9]+)/schedules/$', SystemJobTemplateSchedulesList.as_view(), name='system_job_template_schedules_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', SystemJobTemplateNotificationTemplatesAnyList.as_view(), - name='system_job_template_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_started/$', SystemJobTemplateNotificationTemplatesStartedList.as_view(), name='system_job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', SystemJobTemplateNotificationTemplatesErrorList.as_view(), diff --git a/awx/api/urls/workflow_job_template.py b/awx/api/urls/workflow_job_template.py index 67ac690b60..5429e0fc3e 100644 --- a/awx/api/urls/workflow_job_template.py +++ b/awx/api/urls/workflow_job_template.py @@ -33,8 +33,6 @@ urls = [ url(r'^(?P[0-9]+)/survey_spec/$', WorkflowJobTemplateSurveySpec.as_view(), name='workflow_job_template_survey_spec'), url(r'^(?P[0-9]+)/workflow_nodes/$', WorkflowJobTemplateWorkflowNodesList.as_view(), name='workflow_job_template_workflow_nodes_list'), url(r'^(?P[0-9]+)/activity_stream/$', WorkflowJobTemplateActivityStreamList.as_view(), name='workflow_job_template_activity_stream_list'), - url(r'^(?P[0-9]+)/notification_templates_any/$', WorkflowJobTemplateNotificationTemplatesAnyList.as_view(), - name='workflow_job_template_notification_templates_any_list'), url(r'^(?P[0-9]+)/notification_templates_started/$', WorkflowJobTemplateNotificationTemplatesStartedList.as_view(), name='workflow_job_template_notification_templates_started_list'), url(r'^(?P[0-9]+)/notification_templates_error/$', WorkflowJobTemplateNotificationTemplatesErrorList.as_view(), diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index b494986ac8..092675267d 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -748,7 +748,6 @@ class ProjectNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): model = models.NotificationTemplate serializer_class = serializers.NotificationTemplateSerializer parent_model = models.Project - relationship = 'notification_templates_any' class ProjectNotificationTemplatesStartedList(ProjectNotificationTemplatesAnyList): @@ -2102,7 +2101,6 @@ class InventorySourceNotificationTemplatesAnyList(SubListCreateAttachDetachAPIVi model = models.NotificationTemplate serializer_class = serializers.NotificationTemplateSerializer parent_model = models.InventorySource - relationship = 'notification_templates_any' def post(self, request, *args, **kwargs): parent = self.get_parent_object() @@ -2631,7 +2629,6 @@ class JobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): model = models.NotificationTemplate serializer_class = serializers.NotificationTemplateSerializer parent_model = models.JobTemplate - relationship = 'notification_templates_any' class JobTemplateNotificationTemplatesStartedList(JobTemplateNotificationTemplatesAnyList): @@ -3238,7 +3235,6 @@ class WorkflowJobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachA model = models.NotificationTemplate serializer_class = serializers.NotificationTemplateSerializer parent_model = models.WorkflowJobTemplate - relationship = 'notification_templates_any' class WorkflowJobTemplateNotificationTemplatesStartedList(WorkflowJobTemplateNotificationTemplatesAnyList): @@ -3414,7 +3410,6 @@ class SystemJobTemplateNotificationTemplatesAnyList(SubListCreateAttachDetachAPI model = models.NotificationTemplate serializer_class = serializers.NotificationTemplateSerializer parent_model = models.SystemJobTemplate - relationship = 'notification_templates_any' class SystemJobTemplateNotificationTemplatesStartedList(SystemJobTemplateNotificationTemplatesAnyList): diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py index de5ef998cd..6213e14f63 100644 --- a/awx/api/views/organization.py +++ b/awx/api/views/organization.py @@ -178,7 +178,6 @@ class OrganizationNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView) model = NotificationTemplate serializer_class = NotificationTemplateSerializer parent_model = Organization - relationship = 'notification_templates_any' class OrganizationNotificationTemplatesStartedList(OrganizationNotificationTemplatesAnyList): diff --git a/awx/main/migrations/0081_v360_notify_on_start.py b/awx/main/migrations/0081_v360_notify_on_start.py index 7fc783d20d..f8578e6227 100644 --- a/awx/main/migrations/0081_v360_notify_on_start.py +++ b/awx/main/migrations/0081_v360_notify_on_start.py @@ -22,4 +22,13 @@ class Migration(migrations.Migration): name='notification_templates_started', field=models.ManyToManyField(blank=True, related_name='unifiedjobtemplate_notification_templates_for_started', to='main.NotificationTemplate'), ), +# migrations.RunPython() stuff goes in here + migrations.RemoveField( + model_name='organization', + name='notification_templates_any', + ), + migrations.RemoveField( + model_name='unifiedjobtemplate', + name='notification_templates_any', + ), ] diff --git a/awx/main/models/ad_hoc_commands.py b/awx/main/models/ad_hoc_commands.py index c843b9f577..287955f42e 100644 --- a/awx/main/models/ad_hoc_commands.py +++ b/awx/main/models/ad_hoc_commands.py @@ -163,7 +163,7 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin): all_orgs.add(h.inventory.organization) active_templates = dict(error=set(), success=set(), - any=set()) + started=set()) base_notification_templates = NotificationTemplate.objects for org in all_orgs: for templ in base_notification_templates.filter(organization_notification_templates_for_errors=org): @@ -172,10 +172,7 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin): active_templates['success'].add(templ) for templ in base_notification_templates.filter(organization_notification_templates_for_started=org): active_templates['started'].add(templ) - for templ in base_notification_templates.filter(organization_notification_templates_for_any=org): - active_templates['any'].add(templ) active_templates['error'] = list(active_templates['error']) - active_templates['any'] = list(active_templates['any']) active_templates['success'] = list(active_templates['success']) active_templates['started'] = list(active_templates['started']) return active_templates diff --git a/awx/main/models/base.py b/awx/main/models/base.py index ec36ec8785..9925dc6049 100644 --- a/awx/main/models/base.py +++ b/awx/main/models/base.py @@ -392,13 +392,6 @@ class NotificationFieldsModel(BaseModel): related_name='%(class)s_notification_templates_for_started' ) - notification_templates_any = models.ManyToManyField( - "NotificationTemplate", - blank=True, - related_name='%(class)s_notification_templates_for_any' - ) - - def prevent_search(relation): """ diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 9301c63c1d..5c1f4ef8eb 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -1623,8 +1623,6 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, CustomVirtualE .filter(unifiedjobtemplate_notification_templates_for_started__in=[self])) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success__in=[self])) - any_notification_templates = list(base_notification_templates - .filter(unifiedjobtemplate_notification_templates_for_any__in=[self])) if self.inventory.organization is not None: error_notification_templates = set(error_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_errors=self.inventory.organization))) @@ -1632,12 +1630,9 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, CustomVirtualE .filter(organization_notification_templates_for_started=self.inventory.organization))) success_notification_templates = set(success_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_success=self.inventory.organization))) - any_notification_templates = set(any_notification_templates + list(base_notification_templates - .filter(organization_notification_templates_for_any=self.inventory.organization))) return dict(error=list(error_notification_templates), started=list(started_notification_templates), - success=list(success_notification_templates), - any=list(any_notification_templates)) + success=list(success_notification_templates)) def clean_source(self): # TODO: remove in 3.3 source = self.source diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index f20d074ead..59b26c66e5 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -439,8 +439,6 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour unifiedjobtemplate_notification_templates_for_started__in=[self, self.project])) success_notification_templates = list(base_notification_templates.filter( unifiedjobtemplate_notification_templates_for_success__in=[self, self.project])) - any_notification_templates = list(base_notification_templates.filter( - unifiedjobtemplate_notification_templates_for_any__in=[self, self.project])) # Get Organization NotificationTemplates if self.project is not None and self.project.organization is not None: error_notification_templates = set(error_notification_templates + list(base_notification_templates.filter( @@ -449,12 +447,9 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour organization_notification_templates_for_started=self.project.organization))) success_notification_templates = set(success_notification_templates + list(base_notification_templates.filter( organization_notification_templates_for_success=self.project.organization))) - any_notification_templates = set(any_notification_templates + list(base_notification_templates.filter( - organization_notification_templates_for_any=self.project.organization))) return dict(error=list(error_notification_templates), started=list(started_notification_templates), - success=list(success_notification_templates), - any=list(any_notification_templates)) + success=list(success_notification_templates)) ''' RelatedJobsMixin @@ -1144,12 +1139,9 @@ class SystemJobTemplate(UnifiedJobTemplate, SystemJobOptions): .filter(unifiedjobtemplate_notification_templates_for_started__in=[self])) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success__in=[self])) - any_notification_templates = list(base_notification_templates - .filter(unifiedjobtemplate_notification_templates_for_any__in=[self])) return dict(error=list(error_notification_templates), started=list(started_notification_templates), - success=list(success_notification_templates), - any=list(any_notification_templates)) + success=list(success_notification_templates)) def _accept_or_ignore_job_kwargs(self, _exclude_errors=None, **kwargs): extra_data = kwargs.pop('extra_vars', {}) diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index c4ba9a7f96..eb9121fd21 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -235,18 +235,16 @@ class JobNotificationMixin(object): logger.warn("No notification template defined for emitting notification") notification_templates = None if notification_templates: - all_notification_templates = set() - if status_str != 'running': - all_notification_templates.update(notification_templates.get('any', [])) - notification_template_type = 'any' - elif status_str == 'succeeded': + if status_str == 'succeeded': notification_template_type = 'success' elif status_str == 'running': notification_template_type = 'started' else: notification_template_type = 'error' - all_notification_templates.update(notification_templates.get(notification_template_type, [])) + all_notification_templates = set(notification_templates.get(notification_template_type, [])) if len(all_notification_templates): + # if status_str != 'running': + # all_notification_templates.update(notification_templates.get('any', [])) try: (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() except AttributeError: diff --git a/awx/main/models/projects.py b/awx/main/models/projects.py index 9da401a909..c86b08421d 100644 --- a/awx/main/models/projects.py +++ b/awx/main/models/projects.py @@ -415,8 +415,6 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn .filter(unifiedjobtemplate_notification_templates_for_started=self)) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success=self)) - any_notification_templates = list(base_notification_templates - .filter(unifiedjobtemplate_notification_templates_for_any=self)) # Get Organization NotificationTemplates if self.organization is not None: error_notification_templates = set(error_notification_templates + @@ -428,13 +426,9 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn success_notification_templates = set(success_notification_templates + list(base_notification_templates .filter(organization_notification_templates_for_success=self.organization))) - any_notification_templates = set(any_notification_templates + - list(base_notification_templates - .filter(organization_notification_templates_for_any=self.organization))) return dict(error=list(error_notification_templates), started=list(started_notification_templates), - success=list(success_notification_templates), - any=list(any_notification_templates)) + success=list(success_notification_templates)) def get_absolute_url(self, request=None): return reverse('api:project_detail', kwargs={'pk': self.pk}, request=request) diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py index 3be0938058..e5f36054ff 100644 --- a/awx/main/models/workflow.py +++ b/awx/main/models/workflow.py @@ -423,12 +423,9 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl .filter(unifiedjobtemplate_notification_templates_for_started__in=[self])) success_notification_templates = list(base_notification_templates .filter(unifiedjobtemplate_notification_templates_for_success__in=[self])) - any_notification_templates = list(base_notification_templates - .filter(unifiedjobtemplate_notification_templates_for_any__in=[self])) return dict(error=list(error_notification_templates), started=list(started_notification_templates), - success=list(success_notification_templates), - any=list(any_notification_templates)) + success=list(success_notification_templates)) def create_unified_job(self, **kwargs): workflow_job = super(WorkflowJobTemplate, self).create_unified_job(**kwargs) diff --git a/awx/main/tests/functional/api/test_inventory.py b/awx/main/tests/functional/api/test_inventory.py index 78827dbe34..9f7688ebf3 100644 --- a/awx/main/tests/functional/api/test_inventory.py +++ b/awx/main/tests/functional/api/test_inventory.py @@ -42,9 +42,10 @@ def test_inventory_source_notification_on_cloud_only(get, post, inventory_source not_is = inventory_source_factory("not_ec2") - url = reverse('api:inventory_source_notification_templates_any_list', kwargs={'pk': cloud_is.id}) - response = post(url, dict(id=notification_template.id), u) - assert response.status_code == 204 + # Delete the below portion? + # url = reverse('api:inventory_source_notification_templates_any_list', kwargs={'pk': cloud_is.id}) + # response = post(url, dict(id=notification_template.id), u) + # assert response.status_code == 204 url = reverse('api:inventory_source_notification_templates_success_list', kwargs={'pk': not_is.id}) response = post(url, dict(id=notification_template.id), u) diff --git a/awx/main/tests/functional/test_notifications.py b/awx/main/tests/functional/test_notifications.py index 5c2bf74d5a..fb209f5e77 100644 --- a/awx/main/tests/functional/test_notifications.py +++ b/awx/main/tests/functional/test_notifications.py @@ -92,18 +92,19 @@ def test_inherited_notification_templates(get, post, user, organization, project isrc.save() jt = JobTemplate.objects.create(name='test', inventory=i, project=project, playbook='debug.yml') jt.save() - url = reverse('api:organization_notification_templates_any_list', kwargs={'pk': organization.id}) - response = post(url, dict(id=notification_templates[0]), u) - assert response.status_code == 204 - url = reverse('api:project_notification_templates_any_list', kwargs={'pk': project.id}) - response = post(url, dict(id=notification_templates[1]), u) - assert response.status_code == 204 - url = reverse('api:job_template_notification_templates_any_list', kwargs={'pk': jt.id}) - response = post(url, dict(id=notification_templates[2]), u) - assert response.status_code == 204 - assert len(jt.notification_templates['any']) == 3 - assert len(project.notification_templates['any']) == 2 - assert len(isrc.notification_templates['any']) == 1 + # What can we replace the below tests with? + # url = reverse('api:organization_notification_templates_any_list', kwargs={'pk': organization.id}) + # response = post(url, dict(id=notification_templates[0]), u) + # assert response.status_code == 204 + # url = reverse('api:project_notification_templates_any_list', kwargs={'pk': project.id}) + # response = post(url, dict(id=notification_templates[1]), u) + # assert response.status_code == 204 + # url = reverse('api:job_template_notification_templates_any_list', kwargs={'pk': jt.id}) + # response = post(url, dict(id=notification_templates[2]), u) + # assert response.status_code == 204 + # assert len(jt.notification_templates['any']) == 3 + # assert len(project.notification_templates['any']) == 2 + # assert len(isrc.notification_templates['any']) == 1 @pytest.mark.django_db diff --git a/awx/main/tests/unit/api/serializers/test_inventory_serializers.py b/awx/main/tests/unit/api/serializers/test_inventory_serializers.py index 664f374483..a1191bea2c 100644 --- a/awx/main/tests/unit/api/serializers/test_inventory_serializers.py +++ b/awx/main/tests/unit/api/serializers/test_inventory_serializers.py @@ -71,7 +71,7 @@ class TestInventorySourceSerializerGetRelated(object): 'activity_stream', 'notification_templates_error', 'notification_templates_success', - 'notification_templates_any', + 'notification_templates_started', 'inventory_updates', 'update', 'hosts', diff --git a/awx/main/tests/unit/api/serializers/test_job_template_serializers.py b/awx/main/tests/unit/api/serializers/test_job_template_serializers.py index 3e842a0598..698d27cb7c 100644 --- a/awx/main/tests/unit/api/serializers/test_job_template_serializers.py +++ b/awx/main/tests/unit/api/serializers/test_job_template_serializers.py @@ -50,7 +50,7 @@ class TestJobTemplateSerializerGetRelated(): 'schedules', 'activity_stream', 'launch', - 'notification_templates_any', + 'notification_templates_started', 'notification_templates_success', 'notification_templates_error', 'survey_spec', diff --git a/docs/notification_system.md b/docs/notification_system.md index 6b751beca3..7ef17d2cef 100644 --- a/docs/notification_system.md +++ b/docs/notification_system.md @@ -22,9 +22,7 @@ Notification templates assigned at certain levels will inherit notifications def ## Workflow -When a job starts, succeeds or fails, the running, error or success handler will pull a list of relevant notifications using the procedure defined above. It will then create a Notification object for each one containing relevant details about the job and then **sends** it to the destination (email addresses, slack channel(s), SMS numbers, etc.). These Notification objects are available as related resources on job types (Jobs, Inventory Updates, Project Updates), and also at `/api/v2/notifications`. You may also see what notifications have been sent from a notifications by examining its related resources. - -When a notification is associated to a job via the `/api/v2/.../notification_templates_any/` endpoint, it will send upon success OR fail, but _not_ on start. +When a job starts, succeeds or fails, the running, success or error handler, respectively, will pull a list of relevant notifications using the procedure defined above. It then creates a Notification Object for each one containing relevant details about the job and then **sends** it to the destination (email addresses, Slack channel(s), SMS numbers, etc.). These Notification objects are available as related resources on job types (Jobs, Inventory Updates, Project Updates), and also at `/api/v2/notifications`. You may also see what notifications have been sent by examining its related resources. Notifications can succeed or fail but that will _not_ cause its associated job to succeed or fail. The status of the notification can be viewed at its detail endpoint: `/api/v2/notifications/` From f5b6bd65cf63eab3163290006dfedcf660238f8b Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 13 Jun 2019 11:16:05 -0400 Subject: [PATCH 13/15] More deletions of 'any' state --- awx/api/urls/inventory_source.py | 1 - awx/api/urls/job_template.py | 1 - awx/api/urls/organization.py | 1 - awx/api/urls/project.py | 1 - awx/api/urls/system_job_template.py | 1 - awx/api/urls/workflow_job_template.py | 1 - awx/main/tests/functional/api/test_inventory.py | 2 +- awx/main/tests/functional/test_notifications.py | 15 ++++++++------- 8 files changed, 9 insertions(+), 14 deletions(-) diff --git a/awx/api/urls/inventory_source.py b/awx/api/urls/inventory_source.py index 98c65d77bc..736797c5a7 100644 --- a/awx/api/urls/inventory_source.py +++ b/awx/api/urls/inventory_source.py @@ -13,7 +13,6 @@ from awx.api.views import ( InventorySourceCredentialsList, InventorySourceGroupsList, InventorySourceHostsList, - InventorySourceNotificationTemplatesAnyList, InventorySourceNotificationTemplatesErrorList, InventorySourceNotificationTemplatesStartedList, InventorySourceNotificationTemplatesSuccessList, diff --git a/awx/api/urls/job_template.py b/awx/api/urls/job_template.py index 3d61094eb4..922019d117 100644 --- a/awx/api/urls/job_template.py +++ b/awx/api/urls/job_template.py @@ -13,7 +13,6 @@ from awx.api.views import ( JobTemplateSchedulesList, JobTemplateSurveySpec, JobTemplateActivityStreamList, - JobTemplateNotificationTemplatesAnyList, JobTemplateNotificationTemplatesErrorList, JobTemplateNotificationTemplatesStartedList, JobTemplateNotificationTemplatesSuccessList, diff --git a/awx/api/urls/organization.py b/awx/api/urls/organization.py index 2fed9ca97f..952209423e 100644 --- a/awx/api/urls/organization.py +++ b/awx/api/urls/organization.py @@ -15,7 +15,6 @@ from awx.api.views import ( OrganizationCredentialList, OrganizationActivityStreamList, OrganizationNotificationTemplatesList, - OrganizationNotificationTemplatesAnyList, OrganizationNotificationTemplatesErrorList, OrganizationNotificationTemplatesStartedList, OrganizationNotificationTemplatesSuccessList, diff --git a/awx/api/urls/project.py b/awx/api/urls/project.py index 192c41f54a..c0909873df 100644 --- a/awx/api/urls/project.py +++ b/awx/api/urls/project.py @@ -14,7 +14,6 @@ from awx.api.views import ( ProjectUpdatesList, ProjectActivityStreamList, ProjectSchedulesList, - ProjectNotificationTemplatesAnyList, ProjectNotificationTemplatesErrorList, ProjectNotificationTemplatesStartedList, ProjectNotificationTemplatesSuccessList, diff --git a/awx/api/urls/system_job_template.py b/awx/api/urls/system_job_template.py index 9886c4c14a..396271417d 100644 --- a/awx/api/urls/system_job_template.py +++ b/awx/api/urls/system_job_template.py @@ -9,7 +9,6 @@ from awx.api.views import ( SystemJobTemplateLaunch, SystemJobTemplateJobsList, SystemJobTemplateSchedulesList, - SystemJobTemplateNotificationTemplatesAnyList, SystemJobTemplateNotificationTemplatesErrorList, SystemJobTemplateNotificationTemplatesStartedList, SystemJobTemplateNotificationTemplatesSuccessList, diff --git a/awx/api/urls/workflow_job_template.py b/awx/api/urls/workflow_job_template.py index 5429e0fc3e..0a33a8eaaa 100644 --- a/awx/api/urls/workflow_job_template.py +++ b/awx/api/urls/workflow_job_template.py @@ -13,7 +13,6 @@ from awx.api.views import ( WorkflowJobTemplateSurveySpec, WorkflowJobTemplateWorkflowNodesList, WorkflowJobTemplateActivityStreamList, - WorkflowJobTemplateNotificationTemplatesAnyList, WorkflowJobTemplateNotificationTemplatesErrorList, WorkflowJobTemplateNotificationTemplatesStartedList, WorkflowJobTemplateNotificationTemplatesSuccessList, diff --git a/awx/main/tests/functional/api/test_inventory.py b/awx/main/tests/functional/api/test_inventory.py index 9f7688ebf3..23f8ee4b13 100644 --- a/awx/main/tests/functional/api/test_inventory.py +++ b/awx/main/tests/functional/api/test_inventory.py @@ -42,7 +42,7 @@ def test_inventory_source_notification_on_cloud_only(get, post, inventory_source not_is = inventory_source_factory("not_ec2") - # Delete the below portion? + # Delete the below portion? ****** # url = reverse('api:inventory_source_notification_templates_any_list', kwargs={'pk': cloud_is.id}) # response = post(url, dict(id=notification_template.id), u) # assert response.status_code == 204 diff --git a/awx/main/tests/functional/test_notifications.py b/awx/main/tests/functional/test_notifications.py index fb209f5e77..227b15d9f5 100644 --- a/awx/main/tests/functional/test_notifications.py +++ b/awx/main/tests/functional/test_notifications.py @@ -92,7 +92,7 @@ def test_inherited_notification_templates(get, post, user, organization, project isrc.save() jt = JobTemplate.objects.create(name='test', inventory=i, project=project, playbook='debug.yml') jt.save() - # What can we replace the below tests with? + # What can we replace the below tests with? ****** # url = reverse('api:organization_notification_templates_any_list', kwargs={'pk': organization.id}) # response = post(url, dict(id=notification_templates[0]), u) # assert response.status_code == 204 @@ -107,12 +107,13 @@ def test_inherited_notification_templates(get, post, user, organization, project # assert len(isrc.notification_templates['any']) == 1 -@pytest.mark.django_db -def test_notification_template_merging(get, post, user, organization, project, notification_template): - user('admin-poster', True) - organization.notification_templates_any.add(notification_template) - project.notification_templates_any.add(notification_template) - assert len(project.notification_templates['any']) == 1 +# Delete the test below? ****** +# @pytest.mark.django_db +# def test_notification_template_merging(get, post, user, organization, project, notification_template): +# user('admin-poster', True) +# organization.notification_templates_any.add(notification_template) +# project.notification_templates_any.add(notification_template) +# assert len(project.notification_templates['any']) == 1 @pytest.mark.django_db From 17c89ed4127075d037901a1e846471dab5398e82 Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 13 Jun 2019 14:48:28 -0400 Subject: [PATCH 14/15] Remove tests for 'any' notification state --- .../tests/functional/api/test_inventory.py | 5 ---- .../tests/functional/test_notifications.py | 24 +------------------ 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/awx/main/tests/functional/api/test_inventory.py b/awx/main/tests/functional/api/test_inventory.py index 23f8ee4b13..9340e8993b 100644 --- a/awx/main/tests/functional/api/test_inventory.py +++ b/awx/main/tests/functional/api/test_inventory.py @@ -42,11 +42,6 @@ def test_inventory_source_notification_on_cloud_only(get, post, inventory_source not_is = inventory_source_factory("not_ec2") - # Delete the below portion? ****** - # url = reverse('api:inventory_source_notification_templates_any_list', kwargs={'pk': cloud_is.id}) - # response = post(url, dict(id=notification_template.id), u) - # assert response.status_code == 204 - url = reverse('api:inventory_source_notification_templates_success_list', kwargs={'pk': not_is.id}) response = post(url, dict(id=notification_template.id), u) assert response.status_code == 400 diff --git a/awx/main/tests/functional/test_notifications.py b/awx/main/tests/functional/test_notifications.py index 227b15d9f5..576bc71a7a 100644 --- a/awx/main/tests/functional/test_notifications.py +++ b/awx/main/tests/functional/test_notifications.py @@ -92,29 +92,7 @@ def test_inherited_notification_templates(get, post, user, organization, project isrc.save() jt = JobTemplate.objects.create(name='test', inventory=i, project=project, playbook='debug.yml') jt.save() - # What can we replace the below tests with? ****** - # url = reverse('api:organization_notification_templates_any_list', kwargs={'pk': organization.id}) - # response = post(url, dict(id=notification_templates[0]), u) - # assert response.status_code == 204 - # url = reverse('api:project_notification_templates_any_list', kwargs={'pk': project.id}) - # response = post(url, dict(id=notification_templates[1]), u) - # assert response.status_code == 204 - # url = reverse('api:job_template_notification_templates_any_list', kwargs={'pk': jt.id}) - # response = post(url, dict(id=notification_templates[2]), u) - # assert response.status_code == 204 - # assert len(jt.notification_templates['any']) == 3 - # assert len(project.notification_templates['any']) == 2 - # assert len(isrc.notification_templates['any']) == 1 - - -# Delete the test below? ****** -# @pytest.mark.django_db -# def test_notification_template_merging(get, post, user, organization, project, notification_template): -# user('admin-poster', True) -# organization.notification_templates_any.add(notification_template) -# project.notification_templates_any.add(notification_template) -# assert len(project.notification_templates['any']) == 1 - + @pytest.mark.django_db def test_notification_template_simple_patch(patch, notification_template, admin): From 69502bc133f7fbde7595392648ddd2a672795db3 Mon Sep 17 00:00:00 2001 From: beeankha Date: Mon, 17 Jun 2019 10:46:50 -0400 Subject: [PATCH 15/15] Add functions in migration file for deleting and altering 'any' state notifications --- .../migrations/0081_v360_notify_on_start.py | 18 +++++++++++++++++- awx/main/models/notifications.py | 2 -- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/awx/main/migrations/0081_v360_notify_on_start.py b/awx/main/migrations/0081_v360_notify_on_start.py index f8578e6227..4240905f40 100644 --- a/awx/main/migrations/0081_v360_notify_on_start.py +++ b/awx/main/migrations/0081_v360_notify_on_start.py @@ -5,6 +5,21 @@ from __future__ import unicode_literals from django.db import migrations, models +def forwards_split_unified_job_template_any(apps, schema_editor): + UnifiedJobTemplate = apps.get_model('main', 'unifiedjobtemplate') + for ujt in UnifiedJobTemplate.objects.all(): + for ujt_notification in ujt.notification_templates_any.all(): + ujt.notification_templates_success.add(ujt_notification) + ujt.notification_templates_error.add(ujt_notification) + +def forwards_split_organization_any(apps, schema_editor): + Organization = apps.get_model('main', 'organization') + for org in Organization.objects.all(): + for org_notification in org.notification_templates_any.all(): + org.notification_templates_success.add(org_notification) + org.notification_templates_error.add(org_notification) + + class Migration(migrations.Migration): dependencies = [ @@ -22,7 +37,8 @@ class Migration(migrations.Migration): name='notification_templates_started', field=models.ManyToManyField(blank=True, related_name='unifiedjobtemplate_notification_templates_for_started', to='main.NotificationTemplate'), ), -# migrations.RunPython() stuff goes in here + # Separate out "any" notifications into "success" and "error" before the "any" state gets deleted. + migrations.RunPython(forwards_split_unified_job_template_any, forwards_split_organization_any), migrations.RemoveField( model_name='organization', name='notification_templates_any', diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index eb9121fd21..cdec14f953 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -243,8 +243,6 @@ class JobNotificationMixin(object): notification_template_type = 'error' all_notification_templates = set(notification_templates.get(notification_template_type, [])) if len(all_notification_templates): - # if status_str != 'running': - # all_notification_templates.update(notification_templates.get('any', [])) try: (notification_subject, notification_body) = getattr(self, 'build_notification_%s_message' % status_str)() except AttributeError: