diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 9a6f0a086b..93793a90f2 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -734,6 +734,7 @@ class UnifiedJobSerializer(BaseSerializer): class Meta: model = UnifiedJob + fields = ( '*', 'unified_job_template', @@ -753,7 +754,9 @@ class UnifiedJobSerializer(BaseSerializer): 'controller_node', 'result_traceback', 'event_processing_finished', + 'launched_by', ) + extra_kwargs = { 'unified_job_template': {'source': 'unified_job_template_id', 'label': 'unified job template'}, 'job_env': {'read_only': True, 'label': 'job_env'}, @@ -799,6 +802,16 @@ class UnifiedJobSerializer(BaseSerializer): if val is not None: summary_fields['source_workflow_job'][field] = val + if self.is_detail_view: + ancestor = obj.ancestor_job + if ancestor != obj: + summary_fields['ancestor_job'] = { + 'id': ancestor.id, + 'name': ancestor.name, + 'type': get_type_for_model(ancestor), + 'url': ancestor.get_absolute_url(), + } + return summary_fields def get_sub_serializer(self, obj): @@ -843,6 +856,10 @@ class UnifiedJobSerializer(BaseSerializer): ret['job_explanation'] = _(obj.job_explanation) return ret + def get_launched_by(self, obj): + if obj is not None: + return obj.launched_by + class UnifiedJobListSerializer(UnifiedJobSerializer): class Meta: diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 176896f19b..76a2895bf8 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -1483,3 +1483,29 @@ class UnifiedJob( else: msg = f"{self._meta.model_name}-{self.id} {state.replace('_', ' ')}" logger_job_lifecycle.debug(msg, extra=extra) + + @property + def launched_by(self): + ancestor_job = self.ancestor_job + + if ancestor_job.launch_type == "dependency": + return {'id': None, 'name': 'Generated by AWX', 'type': 'Dependency', 'url': None} + + attr = { + "manual": "created_by", + "relaunch": "created_by", + "scheduled": "schedule", + "workflow": "workflow", + "webhook": "job_template", + "sync": "project", + "scm": "inventory", + } + + obj = getattr(ancestor_job, attr.get(ancestor_job.launch_type, ''), None) + if obj is not None: + return {'id': obj.id, 'name': getattr(obj, 'name', None) or obj.username, 'type': get_type_for_model(obj), 'url': obj.get_absolute_url()} + return {} + + @property + def ancestor_job(self): + return self.get_workflow_job().ancestor_job if self.spawned_by_workflow else self