mirror of
https://github.com/ansible/awx.git
synced 2026-05-09 10:27:37 -02:30
move code linting to a stricter pep8-esque auto-formatting tool, black
This commit is contained in:
@@ -38,15 +38,17 @@ __all__ = ['NotificationTemplate', 'Notification']
|
||||
|
||||
class NotificationTemplate(CommonModelNameNotUnique):
|
||||
|
||||
NOTIFICATION_TYPES = [('email', _('Email'), CustomEmailBackend),
|
||||
('slack', _('Slack'), SlackBackend),
|
||||
('twilio', _('Twilio'), TwilioBackend),
|
||||
('pagerduty', _('Pagerduty'), PagerDutyBackend),
|
||||
('grafana', _('Grafana'), GrafanaBackend),
|
||||
('webhook', _('Webhook'), WebhookBackend),
|
||||
('mattermost', _('Mattermost'), MattermostBackend),
|
||||
('rocketchat', _('Rocket.Chat'), RocketChatBackend),
|
||||
('irc', _('IRC'), IrcBackend)]
|
||||
NOTIFICATION_TYPES = [
|
||||
('email', _('Email'), CustomEmailBackend),
|
||||
('slack', _('Slack'), SlackBackend),
|
||||
('twilio', _('Twilio'), TwilioBackend),
|
||||
('pagerduty', _('Pagerduty'), PagerDutyBackend),
|
||||
('grafana', _('Grafana'), GrafanaBackend),
|
||||
('webhook', _('Webhook'), WebhookBackend),
|
||||
('mattermost', _('Mattermost'), MattermostBackend),
|
||||
('rocketchat', _('Rocket.Chat'), RocketChatBackend),
|
||||
('irc', _('IRC'), IrcBackend),
|
||||
]
|
||||
NOTIFICATION_TYPE_CHOICES = sorted([(x[0], x[1]) for x in NOTIFICATION_TYPES])
|
||||
CLASS_FOR_NOTIFICATION_TYPE = dict([(x[0], x[2]) for x in NOTIFICATION_TYPES])
|
||||
|
||||
@@ -64,7 +66,7 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
)
|
||||
|
||||
notification_type = models.CharField(
|
||||
max_length = 32,
|
||||
max_length=32,
|
||||
choices=NOTIFICATION_TYPE_CHOICES,
|
||||
)
|
||||
|
||||
@@ -73,11 +75,7 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
def default_messages():
|
||||
return {'started': None, 'success': None, 'error': None, 'workflow_approval': None}
|
||||
|
||||
messages = JSONField(
|
||||
null=True,
|
||||
blank=True,
|
||||
default=default_messages,
|
||||
help_text=_('Optional custom messages for notification template.'))
|
||||
messages = JSONField(null=True, blank=True, default=default_messages, help_text=_('Optional custom messages for notification template.'))
|
||||
|
||||
def has_message(self, condition):
|
||||
potential_template = self.messages.get(condition, {})
|
||||
@@ -114,6 +112,7 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
for msg_type in ['message', 'body']:
|
||||
if msg_type not in local_new_event_msgs and local_old_event_msgs.get(msg_type, None):
|
||||
local_new_event_msgs[msg_type] = local_old_event_msgs[msg_type]
|
||||
|
||||
if old_messages is not None and new_messages is not None:
|
||||
for event in ('started', 'success', 'error', 'workflow_approval'):
|
||||
if not new_messages.get(event, {}) and old_messages.get(event, {}):
|
||||
@@ -134,9 +133,7 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
merge_messages(old_messages, new_messages, event)
|
||||
new_messages.setdefault(event, None)
|
||||
|
||||
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password",
|
||||
self.notification_class.init_parameters):
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password", self.notification_class.init_parameters):
|
||||
if self.notification_configuration[field].startswith("$encrypted$"):
|
||||
continue
|
||||
if new_instance:
|
||||
@@ -151,8 +148,7 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
super(NotificationTemplate, self).save(*args, **kwargs)
|
||||
if new_instance:
|
||||
update_fields = []
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password",
|
||||
self.notification_class.init_parameters):
|
||||
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("config", field), '')
|
||||
self.notification_configuration[field] = saved_value
|
||||
if 'notification_configuration' not in update_fields:
|
||||
@@ -164,21 +160,16 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
return self.notification_configuration[self.notification_class.recipient_parameter]
|
||||
|
||||
def generate_notification(self, msg, body):
|
||||
notification = Notification(notification_template=self,
|
||||
notification_type=self.notification_type,
|
||||
recipients=smart_str(self.recipients),
|
||||
subject=msg,
|
||||
body=body)
|
||||
notification = Notification(
|
||||
notification_template=self, notification_type=self.notification_type, recipients=smart_str(self.recipients), subject=msg, body=body
|
||||
)
|
||||
notification.save()
|
||||
return notification
|
||||
|
||||
def send(self, subject, body):
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password",
|
||||
self.notification_class.init_parameters):
|
||||
for field in filter(lambda x: self.notification_class.init_parameters[x]['type'] == "password", self.notification_class.init_parameters):
|
||||
if field in self.notification_configuration:
|
||||
self.notification_configuration[field] = decrypt_field(self,
|
||||
'notification_configuration',
|
||||
subfield=field)
|
||||
self.notification_configuration[field] = decrypt_field(self, 'notification_configuration', subfield=field)
|
||||
recipients = self.notification_configuration.pop(self.notification_class.recipient_parameter)
|
||||
if not isinstance(recipients, list):
|
||||
recipients = [recipients]
|
||||
@@ -202,9 +193,9 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
|
||||
|
||||
class Notification(CreatedModifiedModel):
|
||||
'''
|
||||
"""
|
||||
A notification event emitted when a NotificationTemplate is run
|
||||
'''
|
||||
"""
|
||||
|
||||
NOTIFICATION_STATE_CHOICES = [
|
||||
('pending', _('Pending')),
|
||||
@@ -216,12 +207,7 @@ class Notification(CreatedModifiedModel):
|
||||
app_label = 'main'
|
||||
ordering = ('pk',)
|
||||
|
||||
notification_template = models.ForeignKey(
|
||||
'NotificationTemplate',
|
||||
related_name='notifications',
|
||||
on_delete=models.CASCADE,
|
||||
editable=False
|
||||
)
|
||||
notification_template = models.ForeignKey('NotificationTemplate', related_name='notifications', on_delete=models.CASCADE, editable=False)
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=NOTIFICATION_STATE_CHOICES,
|
||||
@@ -238,7 +224,7 @@ class Notification(CreatedModifiedModel):
|
||||
editable=False,
|
||||
)
|
||||
notification_type = models.CharField(
|
||||
max_length = 32,
|
||||
max_length=32,
|
||||
choices=NotificationTemplate.NOTIFICATION_TYPE_CHOICES,
|
||||
)
|
||||
recipients = models.TextField(
|
||||
@@ -258,112 +244,160 @@ class Notification(CreatedModifiedModel):
|
||||
|
||||
|
||||
class JobNotificationMixin(object):
|
||||
STATUS_TO_TEMPLATE_TYPE = {'succeeded': 'success',
|
||||
'running': 'started',
|
||||
'failed': 'error'}
|
||||
STATUS_TO_TEMPLATE_TYPE = {'succeeded': 'success', 'running': 'started', 'failed': 'error'}
|
||||
# Tree of fields that can be safely referenced in a notification message
|
||||
JOB_FIELDS_ALLOWED_LIST = ['id', 'type', 'url', 'created', 'modified', 'name', 'description', 'job_type', 'playbook',
|
||||
'forks', 'limit', 'verbosity', 'job_tags', 'force_handlers', 'skip_tags', 'start_at_task',
|
||||
'timeout', 'use_fact_cache', 'launch_type', 'status', 'failed', 'started', 'finished',
|
||||
'elapsed', 'job_explanation', 'execution_node', 'controller_node', 'allow_simultaneous',
|
||||
'scm_revision', 'diff_mode', 'job_slice_number', 'job_slice_count', 'custom_virtualenv',
|
||||
'approval_status', 'approval_node_name', 'workflow_url', 'scm_branch', 'artifacts',
|
||||
{'host_status_counts': ['skipped', 'ok', 'changed', 'failed', 'failures', 'dark'
|
||||
'processed', 'rescued', 'ignored']},
|
||||
{'summary_fields': [{'inventory': ['id', 'name', 'description', 'has_active_failures',
|
||||
'total_hosts', 'hosts_with_active_failures', 'total_groups',
|
||||
'has_inventory_sources',
|
||||
'total_inventory_sources', 'inventory_sources_with_failures',
|
||||
'organization_id', 'kind']},
|
||||
{'project': ['id', 'name', 'description', 'status', 'scm_type']},
|
||||
{'job_template': ['id', 'name', 'description']},
|
||||
{'unified_job_template': ['id', 'name', 'description', 'unified_job_type']},
|
||||
{'instance_group': ['name', 'id']},
|
||||
{'created_by': ['id', 'username', 'first_name', 'last_name']},
|
||||
{'schedule': ['id', 'name', 'description', 'next_run']},
|
||||
{'labels': ['count', 'results']}]}]
|
||||
JOB_FIELDS_ALLOWED_LIST = [
|
||||
'id',
|
||||
'type',
|
||||
'url',
|
||||
'created',
|
||||
'modified',
|
||||
'name',
|
||||
'description',
|
||||
'job_type',
|
||||
'playbook',
|
||||
'forks',
|
||||
'limit',
|
||||
'verbosity',
|
||||
'job_tags',
|
||||
'force_handlers',
|
||||
'skip_tags',
|
||||
'start_at_task',
|
||||
'timeout',
|
||||
'use_fact_cache',
|
||||
'launch_type',
|
||||
'status',
|
||||
'failed',
|
||||
'started',
|
||||
'finished',
|
||||
'elapsed',
|
||||
'job_explanation',
|
||||
'execution_node',
|
||||
'controller_node',
|
||||
'allow_simultaneous',
|
||||
'scm_revision',
|
||||
'diff_mode',
|
||||
'job_slice_number',
|
||||
'job_slice_count',
|
||||
'custom_virtualenv',
|
||||
'approval_status',
|
||||
'approval_node_name',
|
||||
'workflow_url',
|
||||
'scm_branch',
|
||||
'artifacts',
|
||||
{'host_status_counts': ['skipped', 'ok', 'changed', 'failed', 'failures', 'dark' 'processed', 'rescued', 'ignored']},
|
||||
{
|
||||
'summary_fields': [
|
||||
{
|
||||
'inventory': [
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
'has_active_failures',
|
||||
'total_hosts',
|
||||
'hosts_with_active_failures',
|
||||
'total_groups',
|
||||
'has_inventory_sources',
|
||||
'total_inventory_sources',
|
||||
'inventory_sources_with_failures',
|
||||
'organization_id',
|
||||
'kind',
|
||||
]
|
||||
},
|
||||
{'project': ['id', 'name', 'description', 'status', 'scm_type']},
|
||||
{'job_template': ['id', 'name', 'description']},
|
||||
{'unified_job_template': ['id', 'name', 'description', 'unified_job_type']},
|
||||
{'instance_group': ['name', 'id']},
|
||||
{'created_by': ['id', 'username', 'first_name', 'last_name']},
|
||||
{'schedule': ['id', 'name', 'description', 'next_run']},
|
||||
{'labels': ['count', 'results']},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def context_stub(cls):
|
||||
"""Returns a stub context that can be used for validating notification messages.
|
||||
Context has the same structure as the context that will actually be used to render
|
||||
a notification message."""
|
||||
context = {'job': {'allow_simultaneous': False,
|
||||
'artifacts': {},
|
||||
'controller_node': 'foo_controller',
|
||||
'created': datetime.datetime(2018, 11, 13, 6, 4, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
'custom_virtualenv': 'my_venv',
|
||||
'description': 'Sample job description',
|
||||
'diff_mode': False,
|
||||
'elapsed': 0.403018,
|
||||
'execution_node': 'awx',
|
||||
'failed': False,
|
||||
'finished': False,
|
||||
'force_handlers': False,
|
||||
'forks': 0,
|
||||
'host_status_counts': {'skipped': 1, 'ok': 5, 'changed': 3, 'failures': 0, 'dark': 0, 'failed': False, 'processed': 0, 'rescued': 0},
|
||||
'id': 42,
|
||||
'job_explanation': 'Sample job explanation',
|
||||
'job_slice_count': 1,
|
||||
'job_slice_number': 0,
|
||||
'job_tags': '',
|
||||
'job_type': 'run',
|
||||
'launch_type': 'workflow',
|
||||
'limit': 'bar_limit',
|
||||
'modified': datetime.datetime(2018, 12, 13, 6, 4, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
'name': 'Stub JobTemplate',
|
||||
'playbook': 'ping.yml',
|
||||
'scm_branch': '',
|
||||
'scm_revision': '',
|
||||
'skip_tags': '',
|
||||
'start_at_task': '',
|
||||
'started': '2019-07-29T17:38:14.137461Z',
|
||||
'status': 'running',
|
||||
'summary_fields': {'created_by': {'first_name': '',
|
||||
'id': 1,
|
||||
'last_name': '',
|
||||
'username': 'admin'},
|
||||
'instance_group': {'id': 1, 'name': 'tower'},
|
||||
'inventory': {'description': 'Sample inventory description',
|
||||
'has_active_failures': False,
|
||||
'has_inventory_sources': False,
|
||||
'hosts_with_active_failures': 0,
|
||||
'id': 17,
|
||||
'inventory_sources_with_failures': 0,
|
||||
'kind': '',
|
||||
'name': 'Stub Inventory',
|
||||
'organization_id': 121,
|
||||
'total_groups': 0,
|
||||
'total_hosts': 1,
|
||||
'total_inventory_sources': 0},
|
||||
'job_template': {'description': 'Sample job template description',
|
||||
'id': 39,
|
||||
'name': 'Stub JobTemplate'},
|
||||
'labels': {'count': 0, 'results': []},
|
||||
'project': {'description': 'Sample project description',
|
||||
'id': 38,
|
||||
'name': 'Stub project',
|
||||
'scm_type': 'git',
|
||||
'status': 'successful'},
|
||||
'schedule': {'description': 'Sample schedule',
|
||||
'id': 42,
|
||||
'name': 'Stub schedule',
|
||||
'next_run': datetime.datetime(2038, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc)},
|
||||
'unified_job_template': {'description': 'Sample unified job template description',
|
||||
'id': 39,
|
||||
'name': 'Stub Job Template',
|
||||
'unified_job_type': 'job'}},
|
||||
'timeout': 0,
|
||||
'type': 'job',
|
||||
'url': '/api/v2/jobs/13/',
|
||||
'use_fact_cache': False,
|
||||
'verbosity': 0},
|
||||
'job_friendly_name': 'Job',
|
||||
'url': 'https://towerhost/#/jobs/playbook/1010',
|
||||
'approval_status': 'approved',
|
||||
'approval_node_name': 'Approve Me',
|
||||
'workflow_url': 'https://towerhost/#/jobs/workflow/1010',
|
||||
'job_metadata': """{'url': 'https://towerhost/$/jobs/playbook/13',
|
||||
context = {
|
||||
'job': {
|
||||
'allow_simultaneous': False,
|
||||
'artifacts': {},
|
||||
'controller_node': 'foo_controller',
|
||||
'created': datetime.datetime(2018, 11, 13, 6, 4, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
'custom_virtualenv': 'my_venv',
|
||||
'description': 'Sample job description',
|
||||
'diff_mode': False,
|
||||
'elapsed': 0.403018,
|
||||
'execution_node': 'awx',
|
||||
'failed': False,
|
||||
'finished': False,
|
||||
'force_handlers': False,
|
||||
'forks': 0,
|
||||
'host_status_counts': {'skipped': 1, 'ok': 5, 'changed': 3, 'failures': 0, 'dark': 0, 'failed': False, 'processed': 0, 'rescued': 0},
|
||||
'id': 42,
|
||||
'job_explanation': 'Sample job explanation',
|
||||
'job_slice_count': 1,
|
||||
'job_slice_number': 0,
|
||||
'job_tags': '',
|
||||
'job_type': 'run',
|
||||
'launch_type': 'workflow',
|
||||
'limit': 'bar_limit',
|
||||
'modified': datetime.datetime(2018, 12, 13, 6, 4, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
'name': 'Stub JobTemplate',
|
||||
'playbook': 'ping.yml',
|
||||
'scm_branch': '',
|
||||
'scm_revision': '',
|
||||
'skip_tags': '',
|
||||
'start_at_task': '',
|
||||
'started': '2019-07-29T17:38:14.137461Z',
|
||||
'status': 'running',
|
||||
'summary_fields': {
|
||||
'created_by': {'first_name': '', 'id': 1, 'last_name': '', 'username': 'admin'},
|
||||
'instance_group': {'id': 1, 'name': 'tower'},
|
||||
'inventory': {
|
||||
'description': 'Sample inventory description',
|
||||
'has_active_failures': False,
|
||||
'has_inventory_sources': False,
|
||||
'hosts_with_active_failures': 0,
|
||||
'id': 17,
|
||||
'inventory_sources_with_failures': 0,
|
||||
'kind': '',
|
||||
'name': 'Stub Inventory',
|
||||
'organization_id': 121,
|
||||
'total_groups': 0,
|
||||
'total_hosts': 1,
|
||||
'total_inventory_sources': 0,
|
||||
},
|
||||
'job_template': {'description': 'Sample job template description', 'id': 39, 'name': 'Stub JobTemplate'},
|
||||
'labels': {'count': 0, 'results': []},
|
||||
'project': {'description': 'Sample project description', 'id': 38, 'name': 'Stub project', 'scm_type': 'git', 'status': 'successful'},
|
||||
'schedule': {
|
||||
'description': 'Sample schedule',
|
||||
'id': 42,
|
||||
'name': 'Stub schedule',
|
||||
'next_run': datetime.datetime(2038, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),
|
||||
},
|
||||
'unified_job_template': {
|
||||
'description': 'Sample unified job template description',
|
||||
'id': 39,
|
||||
'name': 'Stub Job Template',
|
||||
'unified_job_type': 'job',
|
||||
},
|
||||
},
|
||||
'timeout': 0,
|
||||
'type': 'job',
|
||||
'url': '/api/v2/jobs/13/',
|
||||
'use_fact_cache': False,
|
||||
'verbosity': 0,
|
||||
},
|
||||
'job_friendly_name': 'Job',
|
||||
'url': 'https://towerhost/#/jobs/playbook/1010',
|
||||
'approval_status': 'approved',
|
||||
'approval_node_name': 'Approve Me',
|
||||
'workflow_url': 'https://towerhost/#/jobs/workflow/1010',
|
||||
'job_metadata': """{'url': 'https://towerhost/$/jobs/playbook/13',
|
||||
'traceback': '',
|
||||
'status': 'running',
|
||||
'started': '2019-08-07T21:46:38.362630+00:00',
|
||||
@@ -377,7 +411,8 @@ class JobNotificationMixin(object):
|
||||
'friendly_name': 'Job',
|
||||
'finished': False,
|
||||
'credential': 'Stub credential',
|
||||
'created_by': 'admin'}"""}
|
||||
'created_by': 'admin'}""",
|
||||
}
|
||||
|
||||
return context
|
||||
|
||||
@@ -403,11 +438,7 @@ class JobNotificationMixin(object):
|
||||
'job': job_context,
|
||||
'job_friendly_name': self.get_notification_friendly_name(),
|
||||
'url': self.get_ui_url(),
|
||||
'job_metadata': json.dumps(
|
||||
self.notification_data(),
|
||||
ensure_ascii=False,
|
||||
indent=4
|
||||
)
|
||||
'job_metadata': json.dumps(self.notification_data(), ensure_ascii=False, indent=4),
|
||||
}
|
||||
|
||||
def build_context(node, fields, allowed_fields):
|
||||
@@ -425,6 +456,7 @@ class JobNotificationMixin(object):
|
||||
if safe_field not in fields:
|
||||
continue
|
||||
node[safe_field] = fields[safe_field]
|
||||
|
||||
build_context(context['job'], serialized_job, self.JOB_FIELDS_ALLOWED_LIST)
|
||||
|
||||
return context
|
||||
@@ -442,6 +474,7 @@ class JobNotificationMixin(object):
|
||||
env = sandbox.ImmutableSandboxedEnvironment(undefined=ChainableUndefined)
|
||||
|
||||
from awx.api.serializers import UnifiedJobSerializer
|
||||
|
||||
job_serialization = UnifiedJobSerializer(self).to_representation(self)
|
||||
context = self.context(job_serialization)
|
||||
|
||||
@@ -476,6 +509,7 @@ class JobNotificationMixin(object):
|
||||
|
||||
def send_notification_templates(self, status):
|
||||
from awx.main.tasks import send_notifications # avoid circular import
|
||||
|
||||
if status not in ['running', 'succeeded', 'failed']:
|
||||
raise ValueError(_("status must be either running, succeeded or failed"))
|
||||
try:
|
||||
@@ -494,7 +528,8 @@ class JobNotificationMixin(object):
|
||||
# https://stackoverflow.com/a/3431699/10669572
|
||||
def send_it(local_nt=nt, local_msg=msg, local_body=body):
|
||||
def _func():
|
||||
send_notifications.delay([local_nt.generate_notification(local_msg, local_body).id],
|
||||
job_id=self.id)
|
||||
send_notifications.delay([local_nt.generate_notification(local_msg, local_body).id], job_id=self.id)
|
||||
|
||||
return _func
|
||||
|
||||
connection.on_commit(send_it())
|
||||
|
||||
Reference in New Issue
Block a user