mirror of
https://github.com/ansible/awx.git
synced 2026-04-09 20:19:21 -02:30
add new API endpoints and websocket emit for new job event types
see: https://github.com/ansible/awx/issues/200
This commit is contained in:
@@ -1105,6 +1105,7 @@ class ProjectUpdateSerializer(UnifiedJobSerializer, ProjectOptionsSerializer):
|
|||||||
cancel = self.reverse('api:project_update_cancel', kwargs={'pk': obj.pk}),
|
cancel = self.reverse('api:project_update_cancel', kwargs={'pk': obj.pk}),
|
||||||
scm_inventory_updates = self.reverse('api:project_update_scm_inventory_updates', kwargs={'pk': obj.pk}),
|
scm_inventory_updates = self.reverse('api:project_update_scm_inventory_updates', kwargs={'pk': obj.pk}),
|
||||||
notifications = self.reverse('api:project_update_notifications_list', kwargs={'pk': obj.pk}),
|
notifications = self.reverse('api:project_update_notifications_list', kwargs={'pk': obj.pk}),
|
||||||
|
events = self.reverse('api:project_update_events_list', kwargs={'pk': obj.pk}),
|
||||||
))
|
))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -1726,6 +1727,7 @@ class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSeri
|
|||||||
res.update(dict(
|
res.update(dict(
|
||||||
cancel = self.reverse('api:inventory_update_cancel', kwargs={'pk': obj.pk}),
|
cancel = self.reverse('api:inventory_update_cancel', kwargs={'pk': obj.pk}),
|
||||||
notifications = self.reverse('api:inventory_update_notifications_list', kwargs={'pk': obj.pk}),
|
notifications = self.reverse('api:inventory_update_notifications_list', kwargs={'pk': obj.pk}),
|
||||||
|
events = self.reverse('api:inventory_update_events_list', kwargs={'pk': obj.pk}),
|
||||||
))
|
))
|
||||||
if obj.source_project_update_id:
|
if obj.source_project_update_id:
|
||||||
res['source_project_update'] = self.reverse('api:project_update_detail',
|
res['source_project_update'] = self.reverse('api:project_update_detail',
|
||||||
@@ -2962,6 +2964,7 @@ class SystemJobSerializer(UnifiedJobSerializer):
|
|||||||
res['notifications'] = self.reverse('api:system_job_notifications_list', kwargs={'pk': obj.pk})
|
res['notifications'] = self.reverse('api:system_job_notifications_list', kwargs={'pk': obj.pk})
|
||||||
if obj.can_cancel or True:
|
if obj.can_cancel or True:
|
||||||
res['cancel'] = self.reverse('api:system_job_cancel', kwargs={'pk': obj.pk})
|
res['cancel'] = self.reverse('api:system_job_cancel', kwargs={'pk': obj.pk})
|
||||||
|
res['events'] = self.reverse('api:system_job_events_list', kwargs={'pk': obj.pk})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def get_result_stdout(self, obj):
|
def get_result_stdout(self, obj):
|
||||||
@@ -3415,6 +3418,41 @@ class JobEventWebSocketSerializer(JobEventSerializer):
|
|||||||
return 'job_events'
|
return 'job_events'
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateEventSerializer(JobEventSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ProjectUpdateEvent
|
||||||
|
fields = ('*', '-name', '-description', '-job', '-job_id',
|
||||||
|
'-parent_uuid', '-parent', '-host', 'project_update')
|
||||||
|
|
||||||
|
def get_related(self, obj):
|
||||||
|
res = super(JobEventSerializer, self).get_related(obj)
|
||||||
|
res['project_update'] = self.reverse(
|
||||||
|
'api:project_update_detail', kwargs={'pk': obj.project_update_id}
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateEventWebSocketSerializer(ProjectUpdateEventSerializer):
|
||||||
|
created = serializers.SerializerMethodField()
|
||||||
|
modified = serializers.SerializerMethodField()
|
||||||
|
event_name = serializers.CharField(source='event')
|
||||||
|
group_name = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ProjectUpdateEvent
|
||||||
|
fields = ('*', 'event_name', 'group_name',)
|
||||||
|
|
||||||
|
def get_created(self, obj):
|
||||||
|
return obj.created.isoformat()
|
||||||
|
|
||||||
|
def get_modified(self, obj):
|
||||||
|
return obj.modified.isoformat()
|
||||||
|
|
||||||
|
def get_group_name(self, obj):
|
||||||
|
return 'project_update_events'
|
||||||
|
|
||||||
|
|
||||||
class AdHocCommandEventSerializer(BaseSerializer):
|
class AdHocCommandEventSerializer(BaseSerializer):
|
||||||
|
|
||||||
event_display = serializers.CharField(source='get_event_display', read_only=True)
|
event_display = serializers.CharField(source='get_event_display', read_only=True)
|
||||||
@@ -3474,6 +3512,76 @@ class AdHocCommandEventWebSocketSerializer(AdHocCommandEventSerializer):
|
|||||||
return 'ad_hoc_command_events'
|
return 'ad_hoc_command_events'
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryUpdateEventSerializer(AdHocCommandEventSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = InventoryUpdateEvent
|
||||||
|
fields = ('*', '-name', '-description', '-ad_hoc_command', '-host',
|
||||||
|
'-host_name', 'inventory_update')
|
||||||
|
|
||||||
|
def get_related(self, obj):
|
||||||
|
res = super(AdHocCommandEventSerializer, self).get_related(obj)
|
||||||
|
res['inventory_update'] = self.reverse(
|
||||||
|
'api:inventory_update_detail', kwargs={'pk': obj.inventory_update_id}
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryUpdateEventWebSocketSerializer(InventoryUpdateEventSerializer):
|
||||||
|
created = serializers.SerializerMethodField()
|
||||||
|
modified = serializers.SerializerMethodField()
|
||||||
|
event_name = serializers.CharField(source='event')
|
||||||
|
group_name = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = InventoryUpdateEvent
|
||||||
|
fields = ('*', 'event_name', 'group_name',)
|
||||||
|
|
||||||
|
def get_created(self, obj):
|
||||||
|
return obj.created.isoformat()
|
||||||
|
|
||||||
|
def get_modified(self, obj):
|
||||||
|
return obj.modified.isoformat()
|
||||||
|
|
||||||
|
def get_group_name(self, obj):
|
||||||
|
return 'inventory_update_events'
|
||||||
|
|
||||||
|
|
||||||
|
class SystemJobEventSerializer(AdHocCommandEventSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SystemJobEvent
|
||||||
|
fields = ('*', '-name', '-description', '-ad_hoc_command', '-host',
|
||||||
|
'-host_name', 'system_job')
|
||||||
|
|
||||||
|
def get_related(self, obj):
|
||||||
|
res = super(AdHocCommandEventSerializer, self).get_related(obj)
|
||||||
|
res['system_job'] = self.reverse(
|
||||||
|
'api:system_job_detail', kwargs={'pk': obj.system_job_id}
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class SystemJobEventWebSocketSerializer(SystemJobEventSerializer):
|
||||||
|
created = serializers.SerializerMethodField()
|
||||||
|
modified = serializers.SerializerMethodField()
|
||||||
|
event_name = serializers.CharField(source='event')
|
||||||
|
group_name = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SystemJobEvent
|
||||||
|
fields = ('*', 'event_name', 'group_name',)
|
||||||
|
|
||||||
|
def get_created(self, obj):
|
||||||
|
return obj.created.isoformat()
|
||||||
|
|
||||||
|
def get_modified(self, obj):
|
||||||
|
return obj.modified.isoformat()
|
||||||
|
|
||||||
|
def get_group_name(self, obj):
|
||||||
|
return 'system_job_events'
|
||||||
|
|
||||||
|
|
||||||
class JobLaunchSerializer(BaseSerializer):
|
class JobLaunchSerializer(BaseSerializer):
|
||||||
|
|
||||||
# Representational fields
|
# Representational fields
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from awx.api.views import (
|
|||||||
InventoryUpdateCancel,
|
InventoryUpdateCancel,
|
||||||
InventoryUpdateStdout,
|
InventoryUpdateStdout,
|
||||||
InventoryUpdateNotificationsList,
|
InventoryUpdateNotificationsList,
|
||||||
|
InventoryUpdateEventsList,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ urls = [
|
|||||||
url(r'^(?P<pk>[0-9]+)/cancel/$', InventoryUpdateCancel.as_view(), name='inventory_update_cancel'),
|
url(r'^(?P<pk>[0-9]+)/cancel/$', InventoryUpdateCancel.as_view(), name='inventory_update_cancel'),
|
||||||
url(r'^(?P<pk>[0-9]+)/stdout/$', InventoryUpdateStdout.as_view(), name='inventory_update_stdout'),
|
url(r'^(?P<pk>[0-9]+)/stdout/$', InventoryUpdateStdout.as_view(), name='inventory_update_stdout'),
|
||||||
url(r'^(?P<pk>[0-9]+)/notifications/$', InventoryUpdateNotificationsList.as_view(), name='inventory_update_notifications_list'),
|
url(r'^(?P<pk>[0-9]+)/notifications/$', InventoryUpdateNotificationsList.as_view(), name='inventory_update_notifications_list'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/events/$', InventoryUpdateEventsList.as_view(), name='inventory_update_events_list'),
|
||||||
]
|
]
|
||||||
|
|
||||||
__all__ = ['urls']
|
__all__ = ['urls']
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from awx.api.views import (
|
|||||||
ProjectUpdateStdout,
|
ProjectUpdateStdout,
|
||||||
ProjectUpdateScmInventoryUpdates,
|
ProjectUpdateScmInventoryUpdates,
|
||||||
ProjectUpdateNotificationsList,
|
ProjectUpdateNotificationsList,
|
||||||
|
ProjectUpdateEventsList,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ urls = [
|
|||||||
url(r'^(?P<pk>[0-9]+)/stdout/$', ProjectUpdateStdout.as_view(), name='project_update_stdout'),
|
url(r'^(?P<pk>[0-9]+)/stdout/$', ProjectUpdateStdout.as_view(), name='project_update_stdout'),
|
||||||
url(r'^(?P<pk>[0-9]+)/scm_inventory_updates/$', ProjectUpdateScmInventoryUpdates.as_view(), name='project_update_scm_inventory_updates'),
|
url(r'^(?P<pk>[0-9]+)/scm_inventory_updates/$', ProjectUpdateScmInventoryUpdates.as_view(), name='project_update_scm_inventory_updates'),
|
||||||
url(r'^(?P<pk>[0-9]+)/notifications/$', ProjectUpdateNotificationsList.as_view(), name='project_update_notifications_list'),
|
url(r'^(?P<pk>[0-9]+)/notifications/$', ProjectUpdateNotificationsList.as_view(), name='project_update_notifications_list'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/events/$', ProjectUpdateEventsList.as_view(), name='project_update_events_list'),
|
||||||
]
|
]
|
||||||
|
|
||||||
__all__ = ['urls']
|
__all__ = ['urls']
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from awx.api.views import (
|
|||||||
SystemJobDetail,
|
SystemJobDetail,
|
||||||
SystemJobCancel,
|
SystemJobCancel,
|
||||||
SystemJobNotificationsList,
|
SystemJobNotificationsList,
|
||||||
|
SystemJobEventsList
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ urls = [
|
|||||||
url(r'^(?P<pk>[0-9]+)/$', SystemJobDetail.as_view(), name='system_job_detail'),
|
url(r'^(?P<pk>[0-9]+)/$', SystemJobDetail.as_view(), name='system_job_detail'),
|
||||||
url(r'^(?P<pk>[0-9]+)/cancel/$', SystemJobCancel.as_view(), name='system_job_cancel'),
|
url(r'^(?P<pk>[0-9]+)/cancel/$', SystemJobCancel.as_view(), name='system_job_cancel'),
|
||||||
url(r'^(?P<pk>[0-9]+)/notifications/$', SystemJobNotificationsList.as_view(), name='system_job_notifications_list'),
|
url(r'^(?P<pk>[0-9]+)/notifications/$', SystemJobNotificationsList.as_view(), name='system_job_notifications_list'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/events/$', SystemJobEventsList.as_view(), name='system_job_events_list'),
|
||||||
]
|
]
|
||||||
|
|
||||||
__all__ = ['urls']
|
__all__ = ['urls']
|
||||||
|
|||||||
@@ -1366,6 +1366,45 @@ class ProjectUpdateDetail(UnifiedJobDeletionMixin, RetrieveDestroyAPIView):
|
|||||||
new_in_13 = True
|
new_in_13 = True
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateEventsList(SubListAPIView):
|
||||||
|
|
||||||
|
model = ProjectUpdateEvent
|
||||||
|
serializer_class = ProjectUpdateEventSerializer
|
||||||
|
parent_model = ProjectUpdate
|
||||||
|
relationship = 'project_update_events'
|
||||||
|
view_name = _('Project Update Events List')
|
||||||
|
|
||||||
|
def finalize_response(self, request, response, *args, **kwargs):
|
||||||
|
response['X-UI-Max-Events'] = settings.MAX_UI_JOB_EVENTS
|
||||||
|
return super(ProjectUpdateEventsList, self).finalize_response(request, response, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SystemJobEventsList(SubListAPIView):
|
||||||
|
|
||||||
|
model = SystemJobEvent
|
||||||
|
serializer_class = SystemJobEventSerializer
|
||||||
|
parent_model = SystemJob
|
||||||
|
relationship = 'system_job_events'
|
||||||
|
view_name = _('System Job Events List')
|
||||||
|
|
||||||
|
def finalize_response(self, request, response, *args, **kwargs):
|
||||||
|
response['X-UI-Max-Events'] = settings.MAX_UI_JOB_EVENTS
|
||||||
|
return super(SystemJobEventsList, self).finalize_response(request, response, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryUpdateEventsList(SubListAPIView):
|
||||||
|
|
||||||
|
model = InventoryUpdateEvent
|
||||||
|
serializer_class = InventoryUpdateEventSerializer
|
||||||
|
parent_model = InventoryUpdate
|
||||||
|
relationship = 'inventory_update_events'
|
||||||
|
view_name = _('Inventory Update Events List')
|
||||||
|
|
||||||
|
def finalize_response(self, request, response, *args, **kwargs):
|
||||||
|
response['X-UI-Max-Events'] = settings.MAX_UI_JOB_EVENTS
|
||||||
|
return super(InventoryUpdateEventsList, self).finalize_response(request, response, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ProjectUpdateCancel(RetrieveAPIView):
|
class ProjectUpdateCancel(RetrieveAPIView):
|
||||||
|
|
||||||
model = ProjectUpdate
|
model = ProjectUpdate
|
||||||
|
|||||||
@@ -1987,6 +1987,64 @@ class JobEventAccess(BaseAccess):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateEventAccess(BaseAccess):
|
||||||
|
'''
|
||||||
|
I can see project update event records whenever I can access the project update
|
||||||
|
'''
|
||||||
|
|
||||||
|
model = ProjectUpdateEvent
|
||||||
|
|
||||||
|
def filtered_queryset(self):
|
||||||
|
return self.model.objects.filter(
|
||||||
|
Q(project_update__in=ProjectUpdate.accessible_pk_qs(self.user, 'read_role')))
|
||||||
|
|
||||||
|
def can_add(self, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_change(self, obj, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_delete(self, obj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryUpdateEventAccess(BaseAccess):
|
||||||
|
'''
|
||||||
|
I can see inventory update event records whenever I can access the inventory update
|
||||||
|
'''
|
||||||
|
|
||||||
|
model = InventoryUpdateEvent
|
||||||
|
|
||||||
|
def filtered_queryset(self):
|
||||||
|
return self.model.objects.filter(
|
||||||
|
Q(inventory_update__in=InventoryUpdate.accessible_pk_qs(self.user, 'read_role')))
|
||||||
|
|
||||||
|
def can_add(self, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_change(self, obj, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_delete(self, obj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class SystemJobEventAccess(BaseAccess):
|
||||||
|
'''
|
||||||
|
I can only see manage System Jobs events if I'm a super user
|
||||||
|
'''
|
||||||
|
model = SystemJobEvent
|
||||||
|
|
||||||
|
def can_add(self, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_change(self, obj, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_delete(self, obj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class UnifiedJobTemplateAccess(BaseAccess):
|
class UnifiedJobTemplateAccess(BaseAccess):
|
||||||
'''
|
'''
|
||||||
I can see a unified job template whenever I can see the same project,
|
I can see a unified job template whenever I can see the same project,
|
||||||
|
|||||||
@@ -12,11 +12,17 @@ from awx.main.models import (
|
|||||||
UnifiedJob,
|
UnifiedJob,
|
||||||
Job,
|
Job,
|
||||||
AdHocCommand,
|
AdHocCommand,
|
||||||
|
ProjectUpdate,
|
||||||
|
InventoryUpdate,
|
||||||
|
SystemJob
|
||||||
)
|
)
|
||||||
from awx.main.consumers import emit_channel_notification
|
from awx.main.consumers import emit_channel_notification
|
||||||
from awx.api.serializers import (
|
from awx.api.serializers import (
|
||||||
JobEventWebSocketSerializer,
|
JobEventWebSocketSerializer,
|
||||||
AdHocCommandEventWebSocketSerializer,
|
AdHocCommandEventWebSocketSerializer,
|
||||||
|
ProjectUpdateEventWebSocketSerializer,
|
||||||
|
InventoryUpdateEventWebSocketSerializer,
|
||||||
|
SystemJobEventWebSocketSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +66,16 @@ class ReplayJobEvents():
|
|||||||
return self.replay_elapsed().total_seconds() - (self.recording_elapsed(created).total_seconds() * (1.0 / speed))
|
return self.replay_elapsed().total_seconds() - (self.recording_elapsed(created).total_seconds() * (1.0 / speed))
|
||||||
|
|
||||||
def get_job_events(self, job):
|
def get_job_events(self, job):
|
||||||
job_events = job.job_events.order_by('created')
|
if type(job) is Job:
|
||||||
|
job_events = job.job_events.order_by('created')
|
||||||
|
elif type(job) is AdHocCommand:
|
||||||
|
job_events = job.ad_hoc_command_events.order_by('created')
|
||||||
|
elif type(job) is ProjectUpdate:
|
||||||
|
job_events = job.project_update_events.order_by('created')
|
||||||
|
elif type(job) is InventoryUpdate:
|
||||||
|
job_events = job.inventory_update_events.order_by('created')
|
||||||
|
elif type(job) is SystemJob:
|
||||||
|
job_events = job.system_job_events.order_by('created')
|
||||||
if job_events.count() == 0:
|
if job_events.count() == 0:
|
||||||
raise RuntimeError("No events for job id {}".format(job.id))
|
raise RuntimeError("No events for job id {}".format(job.id))
|
||||||
return job_events
|
return job_events
|
||||||
@@ -70,6 +85,12 @@ class ReplayJobEvents():
|
|||||||
return JobEventWebSocketSerializer
|
return JobEventWebSocketSerializer
|
||||||
elif type(job) is AdHocCommand:
|
elif type(job) is AdHocCommand:
|
||||||
return AdHocCommandEventWebSocketSerializer
|
return AdHocCommandEventWebSocketSerializer
|
||||||
|
elif type(job) is ProjectUpdate:
|
||||||
|
return ProjectUpdateEventWebSocketSerializer
|
||||||
|
elif type(job) is InventoryUpdate:
|
||||||
|
return InventoryUpdateEventWebSocketSerializer
|
||||||
|
elif type(job) is SystemJob:
|
||||||
|
return SystemJobEventWebSocketSerializer
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Job is of type {} and replay is not yet supported.".format(type(job)))
|
raise RuntimeError("Job is of type {} and replay is not yet supported.".format(type(job)))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from awx.main.utils import ignore_inventory_computed_fields, ignore_inventory_gr
|
|||||||
from awx.main.tasks import update_inventory_computed_fields
|
from awx.main.tasks import update_inventory_computed_fields
|
||||||
from awx.main.fields import is_implicit_parent
|
from awx.main.fields import is_implicit_parent
|
||||||
|
|
||||||
from awx.main.consumers import emit_channel_notification
|
from awx.main import consumers
|
||||||
|
|
||||||
from awx.conf.utils import conf_to_dict
|
from awx.conf.utils import conf_to_dict
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ def emit_job_event_detail(sender, **kwargs):
|
|||||||
created = kwargs['created']
|
created = kwargs['created']
|
||||||
if created:
|
if created:
|
||||||
event_serialized = JobEventWebSocketSerializer(instance).data
|
event_serialized = JobEventWebSocketSerializer(instance).data
|
||||||
emit_channel_notification('job_events-' + str(instance.job.id), event_serialized)
|
consumers.emit_channel_notification('job_events-' + str(instance.job.id), event_serialized)
|
||||||
|
|
||||||
|
|
||||||
def emit_ad_hoc_command_event_detail(sender, **kwargs):
|
def emit_ad_hoc_command_event_detail(sender, **kwargs):
|
||||||
@@ -56,7 +56,31 @@ def emit_ad_hoc_command_event_detail(sender, **kwargs):
|
|||||||
created = kwargs['created']
|
created = kwargs['created']
|
||||||
if created:
|
if created:
|
||||||
event_serialized = AdHocCommandEventWebSocketSerializer(instance).data
|
event_serialized = AdHocCommandEventWebSocketSerializer(instance).data
|
||||||
emit_channel_notification('ad_hoc_command_events-' + str(instance.ad_hoc_command_id), event_serialized)
|
consumers.emit_channel_notification('ad_hoc_command_events-' + str(instance.ad_hoc_command_id), event_serialized)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_project_update_event_detail(sender, **kwargs):
|
||||||
|
instance = kwargs['instance']
|
||||||
|
created = kwargs['created']
|
||||||
|
if created:
|
||||||
|
event_serialized = ProjectUpdateEventWebSocketSerializer(instance).data
|
||||||
|
consumers.emit_channel_notification('project_update_events-' + str(instance.project_update_id), event_serialized)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_inventory_update_event_detail(sender, **kwargs):
|
||||||
|
instance = kwargs['instance']
|
||||||
|
created = kwargs['created']
|
||||||
|
if created:
|
||||||
|
event_serialized = InventoryUpdateEventWebSocketSerializer(instance).data
|
||||||
|
consumers.emit_channel_notification('inventory_update_events-' + str(instance.inventory_update_id), event_serialized)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_system_job_event_detail(sender, **kwargs):
|
||||||
|
instance = kwargs['instance']
|
||||||
|
created = kwargs['created']
|
||||||
|
if created:
|
||||||
|
event_serialized = SystemJobEventWebSocketSerializer(instance).data
|
||||||
|
consumers.emit_channel_notification('system_job_events-' + str(instance.system_job_id), event_serialized)
|
||||||
|
|
||||||
|
|
||||||
def emit_update_inventory_computed_fields(sender, **kwargs):
|
def emit_update_inventory_computed_fields(sender, **kwargs):
|
||||||
@@ -222,6 +246,9 @@ connect_computed_field_signals()
|
|||||||
|
|
||||||
post_save.connect(emit_job_event_detail, sender=JobEvent)
|
post_save.connect(emit_job_event_detail, sender=JobEvent)
|
||||||
post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent)
|
post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent)
|
||||||
|
post_save.connect(emit_project_update_event_detail, sender=ProjectUpdateEvent)
|
||||||
|
post_save.connect(emit_inventory_update_event_detail, sender=InventoryUpdateEvent)
|
||||||
|
post_save.connect(emit_system_job_event_detail, sender=SystemJobEvent)
|
||||||
m2m_changed.connect(rebuild_role_ancestor_list, Role.parents.through)
|
m2m_changed.connect(rebuild_role_ancestor_list, Role.parents.through)
|
||||||
m2m_changed.connect(org_admin_edit_members, Role.members.through)
|
m2m_changed.connect(org_admin_edit_members, Role.members.through)
|
||||||
m2m_changed.connect(rbac_activity_stream, Role.members.through)
|
m2m_changed.connect(rbac_activity_stream, Role.members.through)
|
||||||
|
|||||||
69
awx/main/tests/functional/models/test_events.py
Normal file
69
awx/main/tests/functional/models/test_events.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import mock
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from awx.main.models import (Job, JobEvent, ProjectUpdate, ProjectUpdateEvent,
|
||||||
|
AdHocCommand, AdHocCommandEvent, InventoryUpdate,
|
||||||
|
InventorySource, InventoryUpdateEvent, SystemJob,
|
||||||
|
SystemJobEvent)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@mock.patch('awx.main.consumers.emit_channel_notification')
|
||||||
|
def test_job_event_websocket_notifications(emit):
|
||||||
|
j = Job(id=123)
|
||||||
|
j.save()
|
||||||
|
JobEvent.create_from_data(job_id=j.pk)
|
||||||
|
assert len(emit.call_args_list) == 1
|
||||||
|
topic, payload = emit.call_args_list[0][0]
|
||||||
|
assert topic == 'job_events-123'
|
||||||
|
assert payload['job'] == 123
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@mock.patch('awx.main.consumers.emit_channel_notification')
|
||||||
|
def test_ad_hoc_event_websocket_notifications(emit):
|
||||||
|
ahc = AdHocCommand(id=123)
|
||||||
|
ahc.save()
|
||||||
|
AdHocCommandEvent.create_from_data(ad_hoc_command_id=ahc.pk)
|
||||||
|
assert len(emit.call_args_list) == 1
|
||||||
|
topic, payload = emit.call_args_list[0][0]
|
||||||
|
assert topic == 'ad_hoc_command_events-123'
|
||||||
|
assert payload['ad_hoc_command'] == 123
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@mock.patch('awx.main.consumers.emit_channel_notification')
|
||||||
|
def test_project_update_event_websocket_notifications(emit, project):
|
||||||
|
pu = ProjectUpdate(id=123, project=project)
|
||||||
|
pu.save()
|
||||||
|
ProjectUpdateEvent.create_from_data(project_update_id=pu.pk)
|
||||||
|
assert len(emit.call_args_list) == 1
|
||||||
|
topic, payload = emit.call_args_list[0][0]
|
||||||
|
assert topic == 'project_update_events-123'
|
||||||
|
assert payload['project_update'] == 123
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@mock.patch('awx.main.consumers.emit_channel_notification')
|
||||||
|
def test_inventory_update_event_websocket_notifications(emit, inventory):
|
||||||
|
source = InventorySource()
|
||||||
|
source.save()
|
||||||
|
iu = InventoryUpdate(id=123, inventory_source=source)
|
||||||
|
iu.save()
|
||||||
|
InventoryUpdateEvent.create_from_data(inventory_update_id=iu.pk)
|
||||||
|
assert len(emit.call_args_list) == 1
|
||||||
|
topic, payload = emit.call_args_list[0][0]
|
||||||
|
assert topic == 'inventory_update_events-123'
|
||||||
|
assert payload['inventory_update'] == 123
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@mock.patch('awx.main.consumers.emit_channel_notification')
|
||||||
|
def test_system_job_event_websocket_notifications(emit, inventory):
|
||||||
|
j = SystemJob(id=123)
|
||||||
|
j.save()
|
||||||
|
SystemJobEvent.create_from_data(system_job_id=j.pk)
|
||||||
|
assert len(emit.call_args_list) == 1
|
||||||
|
topic, payload = emit.call_args_list[0][0]
|
||||||
|
assert topic == 'system_job_events-123'
|
||||||
|
assert payload['system_job'] == 123
|
||||||
46
awx/main/tests/unit/models/test_events.py
Normal file
46
awx/main/tests/unit/models/test_events.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from django.utils.timezone import utc
|
||||||
|
import mock
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from awx.main.models import (JobEvent, ProjectUpdateEvent, AdHocCommandEvent,
|
||||||
|
InventoryUpdateEvent, SystemJobEvent)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('job_identifier, cls', [
|
||||||
|
['job_id', JobEvent],
|
||||||
|
['project_update_id', ProjectUpdateEvent],
|
||||||
|
['ad_hoc_command_id', AdHocCommandEvent],
|
||||||
|
['inventory_update_id', InventoryUpdateEvent],
|
||||||
|
['system_job_id', SystemJobEvent],
|
||||||
|
])
|
||||||
|
@pytest.mark.parametrize('created', [
|
||||||
|
datetime(2018, 1, 1).isoformat(), datetime(2018, 1, 1)
|
||||||
|
])
|
||||||
|
def test_event_parse_created(job_identifier, cls, created):
|
||||||
|
with mock.patch.object(cls, 'objects') as manager:
|
||||||
|
cls.create_from_data(**{
|
||||||
|
job_identifier: 123,
|
||||||
|
'created': created
|
||||||
|
})
|
||||||
|
expected_created = datetime(2018, 1, 1).replace(tzinfo=utc)
|
||||||
|
manager.create.assert_called_with(**{
|
||||||
|
job_identifier: 123,
|
||||||
|
'created': expected_created
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('job_identifier, cls', [
|
||||||
|
['job_id', JobEvent],
|
||||||
|
['project_update_id', ProjectUpdateEvent],
|
||||||
|
['ad_hoc_command_id', AdHocCommandEvent],
|
||||||
|
['inventory_update_id', InventoryUpdateEvent],
|
||||||
|
['system_job_id', SystemJobEvent],
|
||||||
|
])
|
||||||
|
def test_playbook_event_strip_invalid_keys(job_identifier, cls):
|
||||||
|
with mock.patch.object(cls, 'objects') as manager:
|
||||||
|
cls.create_from_data(**{
|
||||||
|
job_identifier: 123,
|
||||||
|
'extra_key': 'extra_value'
|
||||||
|
})
|
||||||
|
manager.create.assert_called_with(**{job_identifier: 123})
|
||||||
Reference in New Issue
Block a user