mirror of
https://github.com/ansible/awx.git
synced 2026-01-19 13:41:28 -03:30
refactor notifications
This commit is contained in:
parent
116e40dbe7
commit
27b9fb8dab
@ -23,13 +23,14 @@ from awx.main.models.base import * # noqa
|
||||
from awx.main.models.unified_jobs import * # noqa
|
||||
from awx.main.utils import decrypt_field
|
||||
from awx.main.conf import tower_settings
|
||||
from awx.main.models.notifications import JobNotificationMixin
|
||||
|
||||
logger = logging.getLogger('awx.main.models.ad_hoc_commands')
|
||||
|
||||
__all__ = ['AdHocCommand', 'AdHocCommandEvent']
|
||||
|
||||
|
||||
class AdHocCommand(UnifiedJob):
|
||||
class AdHocCommand(UnifiedJob, JobNotificationMixin):
|
||||
|
||||
class Meta(object):
|
||||
app_label = 'main'
|
||||
@ -237,6 +238,14 @@ class AdHocCommand(UnifiedJob):
|
||||
update_fields.append('name')
|
||||
super(AdHocCommand, self).save(*args, **kwargs)
|
||||
|
||||
'''
|
||||
JobNotificationMixin
|
||||
'''
|
||||
def get_notification_templates(self):
|
||||
return self.notification_templates
|
||||
|
||||
def get_notification_friendly_name(self):
|
||||
return "AdHoc Command"
|
||||
|
||||
class AdHocCommandEvent(CreatedModifiedModel):
|
||||
'''
|
||||
|
||||
@ -25,7 +25,10 @@ from awx.main.models.base import * # noqa
|
||||
from awx.main.models.jobs import Job
|
||||
from awx.main.models.unified_jobs import * # noqa
|
||||
from awx.main.models.mixins import ResourceMixin
|
||||
from awx.main.models.notifications import NotificationTemplate
|
||||
from awx.main.models.notifications import (
|
||||
NotificationTemplate,
|
||||
JobNotificationMixin,
|
||||
)
|
||||
from awx.main.utils import _inventory_updates
|
||||
from awx.main.conf import tower_settings
|
||||
|
||||
@ -1192,7 +1195,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
|
||||
return source
|
||||
|
||||
|
||||
class InventoryUpdate(UnifiedJob, InventorySourceOptions):
|
||||
class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin):
|
||||
'''
|
||||
Internal job for tracking inventory updates from external sources.
|
||||
'''
|
||||
@ -1268,6 +1271,15 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions):
|
||||
return False
|
||||
return True
|
||||
|
||||
'''
|
||||
JobNotificationMixin
|
||||
'''
|
||||
def get_notification_templates(self):
|
||||
return self.inventory_source.notification_templates
|
||||
|
||||
def get_notification_friendly_name(self):
|
||||
return "Inventory Update"
|
||||
|
||||
|
||||
class CustomInventoryScript(CommonModelNameNotUnique, ResourceMixin):
|
||||
|
||||
|
||||
@ -24,7 +24,10 @@ from jsonfield import JSONField
|
||||
from awx.main.constants import CLOUD_PROVIDERS
|
||||
from awx.main.models.base import * # noqa
|
||||
from awx.main.models.unified_jobs import * # noqa
|
||||
from awx.main.models.notifications import NotificationTemplate
|
||||
from awx.main.models.notifications import (
|
||||
NotificationTemplate,
|
||||
JobNotificationMixin,
|
||||
)
|
||||
from awx.main.utils import decrypt_field, ignore_inventory_computed_fields
|
||||
from awx.main.utils import emit_websocket_notification
|
||||
from awx.main.redact import PlainTextCleaner
|
||||
@ -499,7 +502,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
|
||||
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))
|
||||
|
||||
class Job(UnifiedJob, JobOptions):
|
||||
class Job(UnifiedJob, JobOptions, JobNotificationMixin):
|
||||
'''
|
||||
A job applies a project (with playbook) to an inventory source with a given
|
||||
credential. It represents a single invocation of ansible-playbook with the
|
||||
@ -792,6 +795,15 @@ class Job(UnifiedJob, JobOptions):
|
||||
|
||||
return True
|
||||
|
||||
'''
|
||||
JobNotificationMixin
|
||||
'''
|
||||
def get_notification_templates(self):
|
||||
return self.job_template.notification_templates
|
||||
|
||||
def get_notification_friendly_name(self):
|
||||
return "Job"
|
||||
|
||||
class JobHostSummary(CreatedModifiedModel):
|
||||
'''
|
||||
Per-host statistics for each job.
|
||||
@ -1315,7 +1327,7 @@ class SystemJobTemplate(UnifiedJobTemplate, SystemJobOptions):
|
||||
any=list(any_notification_templates))
|
||||
|
||||
|
||||
class SystemJob(UnifiedJob, SystemJobOptions):
|
||||
class SystemJob(UnifiedJob, SystemJobOptions, JobNotificationMixin):
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
@ -1378,3 +1390,13 @@ class SystemJob(UnifiedJob, SystemJobOptions):
|
||||
@property
|
||||
def task_impact(self):
|
||||
return 150
|
||||
|
||||
'''
|
||||
JobNotificationMixin
|
||||
'''
|
||||
def get_notification_templates(self):
|
||||
return self.system_job_template.notification_templates
|
||||
|
||||
def get_notification_friendly_name(self):
|
||||
return "System Job"
|
||||
|
||||
|
||||
@ -171,3 +171,27 @@ class Notification(CreatedModifiedModel):
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('api:notification_detail', args=(self.pk,))
|
||||
|
||||
class JobNotificationMixin(object):
|
||||
def get_notification_templates(self):
|
||||
raise RuntimeError("Define me")
|
||||
|
||||
def get_notification_friendly_name(self):
|
||||
raise RuntimeError("Define me")
|
||||
|
||||
def _build_notification_message(self, status_str):
|
||||
notification_body = self.notification_data()
|
||||
notification_subject = "{} #{} '{}' {} on Ansible Tower: {}".format(self.get_notification_friendly_name(),
|
||||
self.id,
|
||||
self.name,
|
||||
status_str,
|
||||
notification_body['url'])
|
||||
notification_body['friendly_name'] = self.get_notification_friendly_name()
|
||||
return (notification_subject, notification_body)
|
||||
|
||||
def build_notification_succeeded_message(self):
|
||||
return self._build_notification_message('succeeded')
|
||||
|
||||
def build_notification_failed_message(self):
|
||||
return self._build_notification_message('failed')
|
||||
|
||||
|
||||
@ -20,7 +20,10 @@ from django.utils.timezone import now, make_aware, get_default_timezone
|
||||
from awx.lib.compat import slugify
|
||||
from awx.main.models.base import * # noqa
|
||||
from awx.main.models.jobs import Job
|
||||
from awx.main.models.notifications import NotificationTemplate
|
||||
from awx.main.models.notifications import (
|
||||
NotificationTemplate,
|
||||
JobNotificationMixin,
|
||||
)
|
||||
from awx.main.models.unified_jobs import * # noqa
|
||||
from awx.main.models.mixins import ResourceMixin
|
||||
from awx.main.utils import update_scm_url
|
||||
@ -372,8 +375,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
||||
def get_absolute_url(self):
|
||||
return reverse('api:project_detail', args=(self.pk,))
|
||||
|
||||
|
||||
class ProjectUpdate(UnifiedJob, ProjectOptions):
|
||||
class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin):
|
||||
'''
|
||||
Internal job for tracking project updates from SCM.
|
||||
'''
|
||||
@ -443,3 +445,12 @@ class ProjectUpdate(UnifiedJob, ProjectOptions):
|
||||
if 'scm_delete_on_next_update' not in update_fields:
|
||||
update_fields.append('scm_delete_on_next_update')
|
||||
parent_instance.save(update_fields=update_fields)
|
||||
|
||||
'''
|
||||
JobNotificationMixin
|
||||
'''
|
||||
def get_notification_templates(self):
|
||||
return self.project.notification_templates
|
||||
|
||||
def get_notification_friendly_name(self):
|
||||
return "Project Update"
|
||||
|
||||
@ -18,6 +18,7 @@ from django.core.exceptions import NON_FIELD_ERRORS
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.timezone import now
|
||||
from django.utils.encoding import smart_text
|
||||
from django.apps import apps
|
||||
|
||||
# Django-JSONField
|
||||
from jsonfield import JSONField
|
||||
@ -360,8 +361,30 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
|
||||
dest_field.add(*list(src_field_value.all().values_list('id', flat=True)))
|
||||
return unified_job
|
||||
|
||||
class UnifiedJobTypeStringMixin(object):
|
||||
@classmethod
|
||||
def _underscore_to_camel(cls, word):
|
||||
return ''.join(x.capitalize() or '_' for x in word.split('_'))
|
||||
|
||||
class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique):
|
||||
@classmethod
|
||||
def _model_type(cls, job_type):
|
||||
# Django >= 1.9
|
||||
#app = apps.get_app_config('main')
|
||||
model_str = cls._underscore_to_camel(job_type)
|
||||
try:
|
||||
return apps.get_model('main', model_str)
|
||||
except LookupError:
|
||||
print("Lookup model error")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_instance_by_type(cls, job_type, job_id):
|
||||
model = cls._model_type(job_type)
|
||||
if not model:
|
||||
return None
|
||||
return model.objects.get(id=job_id)
|
||||
|
||||
class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique, UnifiedJobTypeStringMixin):
|
||||
'''
|
||||
Concrete base class for unified job run by the task engine.
|
||||
'''
|
||||
|
||||
@ -185,114 +185,61 @@ def notify_task_runner(metadata_dict):
|
||||
queue = FifoQueue('tower_task_manager')
|
||||
queue.push(metadata_dict)
|
||||
|
||||
|
||||
def _send_notification_templates(instance, status_str):
|
||||
if status_str not in ['succeeded', 'failed']:
|
||||
raise ValueError("status_str must be either succeeded or failed")
|
||||
print("Instance has some shit in it %s" % instance)
|
||||
notification_templates = instance.get_notification_templates()
|
||||
if notification_templates:
|
||||
all_notification_templates = set(notification_templates.get('success', []) + notification_templates.get('any', []))
|
||||
if len(all_notification_templates):
|
||||
try:
|
||||
(notification_subject, notification_body) = getattr(instance, 'build_notification_%s_message' % status_str)()
|
||||
except AttributeError:
|
||||
raise NotImplementedError("build_notification_%s_message() does not exist" % status_str)
|
||||
send_notifications.delay([n.generate_notification(notification_subject, notification_body).id
|
||||
for n in all_notification_templates],
|
||||
job_id=instance.id)
|
||||
|
||||
@task(bind=True)
|
||||
def handle_work_success(self, result, task_actual):
|
||||
if task_actual['type'] == 'project_update':
|
||||
instance = ProjectUpdate.objects.get(id=task_actual['id'])
|
||||
instance_name = instance.name
|
||||
notification_templates = instance.project.notification_templates
|
||||
friendly_name = "Project Update"
|
||||
elif task_actual['type'] == 'inventory_update':
|
||||
instance = InventoryUpdate.objects.get(id=task_actual['id'])
|
||||
instance_name = instance.name
|
||||
notification_templates = instance.inventory_source.notification_templates
|
||||
friendly_name = "Inventory Update"
|
||||
elif task_actual['type'] == 'job':
|
||||
instance = Job.objects.get(id=task_actual['id'])
|
||||
instance_name = instance.job_template.name
|
||||
notification_templates = instance.job_template.notification_templates
|
||||
friendly_name = "Job"
|
||||
elif task_actual['type'] == 'ad_hoc_command':
|
||||
instance = AdHocCommand.objects.get(id=task_actual['id'])
|
||||
instance_name = instance.module_name
|
||||
notification_templates = instance.notification_templates
|
||||
friendly_name = "AdHoc Command"
|
||||
elif task_actual['type'] == 'system_job':
|
||||
instance = SystemJob.objects.get(id=task_actual['id'])
|
||||
instance_name = instance.system_job_template.name
|
||||
notification_templates = instance.system_job_template.notification_templates
|
||||
friendly_name = "System Job"
|
||||
else:
|
||||
instance = UnifiedJob.get_instance_by_type(task_actual['type'], task_actual['id'])
|
||||
if not instance:
|
||||
return
|
||||
|
||||
all_notification_templates = set(notification_templates.get('success', []) + notification_templates.get('any', []))
|
||||
if len(all_notification_templates):
|
||||
notification_body = instance.notification_data()
|
||||
notification_subject = "{} #{} '{}' succeeded on Ansible Tower: {}".format(friendly_name,
|
||||
task_actual['id'],
|
||||
smart_str(instance_name),
|
||||
notification_body['url'])
|
||||
notification_body['friendly_name'] = friendly_name
|
||||
send_notifications.delay([n.generate_notification(notification_subject, notification_body).id
|
||||
for n in all_notification_templates],
|
||||
job_id=task_actual['id'])
|
||||
_send_notification_templates(instance, 'succeeded')
|
||||
|
||||
@task(bind=True)
|
||||
def handle_work_error(self, task_id, subtasks=None):
|
||||
print('Executing error task id %s, subtasks: %s' %
|
||||
(str(self.request.id), str(subtasks)))
|
||||
first_task = None
|
||||
first_task_id = None
|
||||
first_task_type = ''
|
||||
first_task_name = ''
|
||||
first_instance = None
|
||||
first_instance_type = ''
|
||||
if subtasks is not None:
|
||||
for each_task in subtasks:
|
||||
instance_name = ''
|
||||
if each_task['type'] == 'project_update':
|
||||
instance = ProjectUpdate.objects.get(id=each_task['id'])
|
||||
instance_name = instance.name
|
||||
notification_templates = instance.project.notification_templates
|
||||
friendly_name = "Project Update"
|
||||
elif each_task['type'] == 'inventory_update':
|
||||
instance = InventoryUpdate.objects.get(id=each_task['id'])
|
||||
instance_name = instance.name
|
||||
notification_templates = instance.inventory_source.notification_templates
|
||||
friendly_name = "Inventory Update"
|
||||
elif each_task['type'] == 'job':
|
||||
instance = Job.objects.get(id=each_task['id'])
|
||||
instance_name = instance.job_template.name
|
||||
notification_templates = instance.job_template.notification_templates
|
||||
friendly_name = "Job"
|
||||
elif each_task['type'] == 'ad_hoc_command':
|
||||
instance = AdHocCommand.objects.get(id=each_task['id'])
|
||||
instance_name = instance.module_name
|
||||
notification_templates = instance.notification_templates
|
||||
friendly_name = "AdHoc Command"
|
||||
elif each_task['type'] == 'system_job':
|
||||
instance = SystemJob.objects.get(id=each_task['id'])
|
||||
instance_name = instance.system_job_template.name
|
||||
notification_templates = instance.system_job_template.notification_templates
|
||||
friendly_name = "System Job"
|
||||
else:
|
||||
instance = UnifiedJob.get_instance_by_type(each_task['type'], each_task['id'])
|
||||
if not instance:
|
||||
# Unknown task type
|
||||
logger.warn("Unknown task type: {}".format(each_task['type']))
|
||||
continue
|
||||
if first_task is None:
|
||||
first_task = instance
|
||||
first_task_id = instance.id
|
||||
first_task_type = each_task['type']
|
||||
first_task_name = instance_name
|
||||
first_task_friendly_name = friendly_name
|
||||
|
||||
if first_instance is None:
|
||||
first_instance = instance
|
||||
first_instance_type = each_task['type']
|
||||
|
||||
if instance.celery_task_id != task_id:
|
||||
instance.status = 'failed'
|
||||
instance.failed = True
|
||||
instance.job_explanation = 'Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \
|
||||
(first_task_type, first_task_name, first_task_id)
|
||||
(first_instance_type, first_instance.name, first_instance.id)
|
||||
instance.save()
|
||||
instance.socketio_emit_status("failed")
|
||||
|
||||
all_notification_templates = set(notification_templates.get('error', []) + notification_templates.get('any', []))
|
||||
if len(all_notification_templates):
|
||||
notification_body = first_task.notification_data()
|
||||
notification_subject = "{} #{} '{}' failed on Ansible Tower: {}".format(first_task_friendly_name,
|
||||
first_task_id,
|
||||
smart_str(first_task_name),
|
||||
notification_body['url'])
|
||||
notification_body['friendly_name'] = first_task_friendly_name
|
||||
send_notifications.delay([n.generate_notification(notification_subject, notification_body).id
|
||||
for n in all_notification_templates],
|
||||
job_id=first_task_id)
|
||||
|
||||
if first_instance:
|
||||
print("Instance type is %s" % first_instance_type)
|
||||
print("Instance passing along %s" % first_instance.name)
|
||||
_send_notification_templates(first_instance, 'failed')
|
||||
|
||||
@task()
|
||||
def update_inventory_computed_fields(inventory_id, should_update_hosts=True):
|
||||
@ -1710,3 +1657,4 @@ class RunSystemJob(BaseTask):
|
||||
|
||||
def build_cwd(self, instance, **kwargs):
|
||||
return settings.BASE_DIR
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user