mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 16:51:11 +03:00
Refactor message generator
* Job object can now control the output and generate K:V output for notification types that can support it * Notifications store the body as json/dict now to encode more information * Notification Type can further compose the message based on what is sensible for the notification type * This will also allow customizing the message template in the future * All notification types use sane defaults for the level of detail now
This commit is contained in:
parent
eb3d663d18
commit
ab3669efa9
@ -2118,7 +2118,7 @@ class NotificationSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = Notification
|
||||
fields = ('*', '-name', '-description', 'notifier', 'error', 'status', 'notifications_sent',
|
||||
'notification_type', 'recipients', 'subject', 'body')
|
||||
'notification_type', 'recipients', 'subject')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(NotificationSerializer, self).get_related(obj)
|
||||
|
@ -3053,7 +3053,8 @@ class NotifierTest(GenericAPIView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
notification = obj.generate_notification("Tower Notification Test", "Ansible Tower Test Notification")
|
||||
notification = obj.generate_notification("Tower Notification Test {}".format(obj.id),
|
||||
{"body": "Ansible Tower Test Notification {}".format(obj.id)})
|
||||
if not notification:
|
||||
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
|
@ -496,6 +496,26 @@ class Job(UnifiedJob, JobOptions):
|
||||
dependencies.append(source.create_inventory_update(launch_type='dependency'))
|
||||
return dependencies
|
||||
|
||||
def notification_data(self):
|
||||
data = super(Job, self).notification_data()
|
||||
all_hosts = {}
|
||||
for h in self.job_host_summaries.all():
|
||||
all_hosts[h.host.name] = dict(failed=h.failed,
|
||||
changed=h.changed,
|
||||
dark=h.dark,
|
||||
failures=h.failures,
|
||||
ok=h.ok,
|
||||
processed=h.processed,
|
||||
skipped=h.skipped)
|
||||
data.update(dict(inventory=self.inventory.name,
|
||||
project=self.project.name,
|
||||
playbook=self.playbook,
|
||||
credential=self.credential.name,
|
||||
limit=self.limit,
|
||||
extra_vars=self.extra_vars,
|
||||
hosts=all_hosts))
|
||||
return data
|
||||
|
||||
def handle_extra_data(self, extra_data):
|
||||
extra_vars = {}
|
||||
if type(extra_data) == dict:
|
||||
|
@ -69,8 +69,8 @@ class Notifier(CommonModel):
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password",
|
||||
self.notification_class.init_parameters):
|
||||
if new_instance:
|
||||
value = getattr(self.notification_configuration, field, '')
|
||||
setattr(self, '_saved_{}'.format(field), value)
|
||||
value = self.notification_configuration[field]
|
||||
setattr(self, '_saved_{}_{}'.format("config", field), value)
|
||||
self.notification_configuration[field] = ''
|
||||
else:
|
||||
encrypted = encrypt_field(self, 'notification_configuration', subfield=field)
|
||||
@ -82,8 +82,9 @@ class Notifier(CommonModel):
|
||||
update_fields = []
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password",
|
||||
self.notification_class.init_parameters):
|
||||
saved_value = getattr(self, '_saved_{}'.format(field), '')
|
||||
setattr(self.notification_configuration, field, saved_value)
|
||||
saved_value = getattr(self, '_saved_{}_{}'.format("config", field), '')
|
||||
self.notification_configuration[field] = saved_value
|
||||
#setattr(self.notification_configuration, field, saved_value)
|
||||
if 'notification_configuration' not in update_fields:
|
||||
update_fields.append('notification_configuration')
|
||||
self.save(update_fields=update_fields)
|
||||
@ -112,7 +113,7 @@ class Notifier(CommonModel):
|
||||
recipients = [recipients]
|
||||
sender = self.notification_configuration.pop(self.notification_class.sender_parameter, None)
|
||||
backend_obj = self.notification_class(**self.notification_configuration)
|
||||
notification_obj = EmailMessage(subject, body, sender, recipients)
|
||||
notification_obj = EmailMessage(subject, backend_obj.format_body(body), sender, recipients)
|
||||
return backend_obj.send_messages([notification_obj])
|
||||
|
||||
class Notification(CreatedModifiedModel):
|
||||
@ -165,11 +166,7 @@ class Notification(CreatedModifiedModel):
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
body = models.TextField(
|
||||
blank=True,
|
||||
default='',
|
||||
editable=False,
|
||||
)
|
||||
body = JSONField(blank=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('api:notification_detail', args=(self.pk,))
|
||||
|
@ -731,6 +731,16 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
||||
tasks that might preclude creating one'''
|
||||
return []
|
||||
|
||||
def notification_data(self):
|
||||
return dict(id=self.id,
|
||||
name=self.name,
|
||||
url=self.get_absolute_url(), #TODO: Need to replace with UI job view
|
||||
created_by=str(self.created_by),
|
||||
started=self.started.isoformat(),
|
||||
finished=self.finished.isoformat(),
|
||||
status=self.status,
|
||||
traceback=self.result_traceback)
|
||||
|
||||
def start(self, error_callback, success_callback, **kwargs):
|
||||
'''
|
||||
Start the task running via Celery.
|
||||
|
@ -18,3 +18,10 @@ class CustomEmailBackend(EmailBackend):
|
||||
recipient_parameter = "recipients"
|
||||
sender_parameter = "sender"
|
||||
|
||||
def format_body(self, body):
|
||||
body_actual = "{} #{} had status {} on Ansible Tower, view details at {}\n\n".format(body['friendly_name'],
|
||||
body['id'],
|
||||
body['status'],
|
||||
body['url'])
|
||||
body_actual += pprint.pformat(body, indent=4)
|
||||
return body_actual
|
||||
|
@ -5,11 +5,11 @@ import logging
|
||||
|
||||
import requests
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
from awx.main.notifications.base import TowerBaseEmailBackend
|
||||
|
||||
logger = logging.getLogger('awx.main.notifications.hipchat_backend')
|
||||
|
||||
class HipChatBackend(BaseEmailBackend):
|
||||
class HipChatBackend(TowerBaseEmailBackend):
|
||||
|
||||
init_parameters = {"token": {"label": "Token", "type": "password"},
|
||||
"channels": {"label": "Destination Channels", "type": "list"},
|
||||
@ -35,7 +35,7 @@ class HipChatBackend(BaseEmailBackend):
|
||||
r = requests.post("{}/v2/room/{}/notification".format(self.api_url, rcp),
|
||||
params={"auth_token": self.token},
|
||||
json={"color": self.color,
|
||||
"message": m.body,
|
||||
"message": m.subject,
|
||||
"notify": self.notify,
|
||||
"from": m.from_email,
|
||||
"message_format": "text"})
|
||||
|
@ -7,11 +7,11 @@ import logging
|
||||
|
||||
import irc.client
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
from awx.main.notifications.base import TowerBaseEmailBackend
|
||||
|
||||
logger = logging.getLogger('awx.main.notifications.irc_backend')
|
||||
|
||||
class IrcBackend(BaseEmailBackend):
|
||||
class IrcBackend(TowerBaseEmailBackend):
|
||||
|
||||
init_parameters = {"server": {"label": "IRC Server Address", "type": "string"},
|
||||
"port": {"label": "IRC Server Port", "type": "int"},
|
||||
|
@ -4,11 +4,11 @@
|
||||
import logging
|
||||
import pygerduty
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
from awx.main.notifications.base import TowerBaseEmailBackend
|
||||
|
||||
logger = logging.getLogger('awx.main.notifications.pagerduty_backend')
|
||||
|
||||
class PagerDutyBackend(BaseEmailBackend):
|
||||
class PagerDutyBackend(TowerBaseEmailBackend):
|
||||
|
||||
init_parameters = {"subdomain": {"label": "Pagerduty subdomain", "type": "string"},
|
||||
"token": {"label": "API Token", "type": "password"},
|
||||
@ -22,6 +22,9 @@ class PagerDutyBackend(BaseEmailBackend):
|
||||
self.subdomain = subdomain
|
||||
self.token = token
|
||||
|
||||
def format_body(self, body):
|
||||
return body
|
||||
|
||||
def send_messages(self, messages):
|
||||
sent_messages = 0
|
||||
|
||||
|
@ -4,11 +4,11 @@
|
||||
import logging
|
||||
from slackclient import SlackClient
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
from awx.main.notifications.base import TowerBaseEmailBackend
|
||||
|
||||
logger = logging.getLogger('awx.main.notifications.slack_backend')
|
||||
|
||||
class SlackBackend(BaseEmailBackend):
|
||||
class SlackBackend(TowerBaseEmailBackend):
|
||||
|
||||
init_parameters = {"token": {"label": "Token", "type": "password"},
|
||||
"channels": {"label": "Destination Channels", "type": "list"}}
|
||||
@ -41,7 +41,7 @@ class SlackBackend(BaseEmailBackend):
|
||||
for m in messages:
|
||||
try:
|
||||
for r in m.recipients():
|
||||
self.connection.rtm_send_message(r, m.body)
|
||||
self.connection.rtm_send_message(r, m.subject)
|
||||
sent_messages += 1
|
||||
except Exception as e:
|
||||
logger.error("Exception sending messages: {}".format(e))
|
||||
|
@ -5,11 +5,11 @@ import logging
|
||||
|
||||
from twilio.rest import TwilioRestClient
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
from awx.main.notifications.base import TowerBaseEmailBackend
|
||||
|
||||
logger = logging.getLogger('awx.main.notifications.twilio_backend')
|
||||
|
||||
class TwilioBackend(BaseEmailBackend):
|
||||
class TwilioBackend(TowerBaseEmailBackend):
|
||||
|
||||
init_parameters = {"account_sid": {"label": "Account SID", "type": "string"},
|
||||
"account_token": {"label": "Account Token", "type": "password"},
|
||||
@ -38,7 +38,7 @@ class TwilioBackend(BaseEmailBackend):
|
||||
connection.messages.create(
|
||||
to=m.to,
|
||||
from_=m.from_email,
|
||||
body=m.body)
|
||||
body=m.subject)
|
||||
sent_messages += 1
|
||||
except Exception as e:
|
||||
logger.error("Exception sending messages: {}".format(e))
|
||||
|
@ -4,12 +4,12 @@
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
import json
|
||||
from awx.main.notifications.base import TowerBaseEmailBackend
|
||||
|
||||
logger = logging.getLogger('awx.main.notifications.webhook_backend')
|
||||
|
||||
class WebhookBackend(BaseEmailBackend):
|
||||
class WebhookBackend(TowerBaseEmailBackend):
|
||||
|
||||
init_parameters = {"url": {"label": "Target URL", "type": "string"},
|
||||
"headers": {"label": "HTTP Headers", "type": "object"}}
|
||||
@ -20,11 +20,16 @@ class WebhookBackend(BaseEmailBackend):
|
||||
self.headers = headers
|
||||
super(WebhookBackend, self).__init__(fail_silently=fail_silently)
|
||||
|
||||
def format_body(self, body):
|
||||
logger.error("Generating body from {}".format(str(body)))
|
||||
return body
|
||||
|
||||
def send_messages(self, messages):
|
||||
sent_messages = 0
|
||||
for m in messages:
|
||||
logger.error("BODY: " + str(m.body))
|
||||
r = requests.post("{}".format(m.recipients()[0]),
|
||||
data=m.body,
|
||||
data=json.dumps(m.body),
|
||||
headers=self.headers)
|
||||
if r.status_code >= 400:
|
||||
logger.error("Error sending notification webhook: {}".format(r.text))
|
||||
|
@ -207,10 +207,8 @@ def handle_work_success(self, result, task_actual):
|
||||
notification_subject = "{} #{} '{}' succeeded on Ansible Tower".format(friendly_name,
|
||||
task_actual['id'],
|
||||
instance_name)
|
||||
notification_body = "{} #{} '{}' succeeded on Ansible Tower\nTo view the output: {}".format(friendly_name,
|
||||
task_actual['id'],
|
||||
instance_name,
|
||||
instance.get_absolute_url())
|
||||
notification_body = instance.notification_data()
|
||||
notification_body['friendly_name'] = friendly_name
|
||||
send_notifications.delay([n.generate_notification(notification_subject, notification_body)
|
||||
for n in notifiers.get('success', []) + notifiers.get('any', [])],
|
||||
job_id=task_actual['id'])
|
||||
@ -265,10 +263,8 @@ def handle_work_error(self, task_id, subtasks=None):
|
||||
notification_subject = "{} #{} '{}' failed on Ansible Tower".format(first_task_friendly_name,
|
||||
first_task_id,
|
||||
first_task_name)
|
||||
notification_body = "{} #{} '{}' failed on Ansible Tower\nTo view the output: {}".format(first_task_friendly_name,
|
||||
first_task_id,
|
||||
first_task_name,
|
||||
first_task.get_absolute_url())
|
||||
notification_body = first_task.notification_data()
|
||||
notification_body['friendly_name'] = first_task_friendly_name
|
||||
send_notifications.delay([n.generate_notification(notification_subject, notification_body).id
|
||||
for n in notifiers.get('error', []) + notifiers.get('any', [])],
|
||||
job_id=first_task_id)
|
||||
|
Loading…
Reference in New Issue
Block a user