diff --git a/awx/api/views.py b/awx/api/views.py index 0e957393c4..36b6d213c1 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1660,7 +1660,7 @@ class UnifiedJobStdout(RetrieveAPIView): def retrieve(self, request, *args, **kwargs): unified_job = self.get_object() - if request.accepted_renderer.format in ('html', 'api'): + if request.accepted_renderer.format in ('html', 'api', 'json'): scheme = request.QUERY_PARAMS.get('scheme', None) start_line = request.QUERY_PARAMS.get('start_line', 0) end_line = request.QUERY_PARAMS.get('end_line', None) @@ -1668,20 +1668,24 @@ class UnifiedJobStdout(RetrieveAPIView): scheme = 'ansi2html' dark_val = request.QUERY_PARAMS.get('dark', '') dark = bool(dark_val and dark_val[0].lower() in ('1', 't', 'y')) - content_only = bool(request.accepted_renderer.format == 'api') + content_only = bool(request.accepted_renderer.format == 'api' or \ + request.accepted_renderer.format == 'json') dark_bg = (content_only and dark) or (not content_only and (dark or not dark_val)) conv = Ansi2HTMLConverter(scheme=scheme, dark_bg=dark_bg, title=get_view_name(self.__class__)) + content, start, end = unified_job.result_stdout_raw_limited(start_line, end_line) if content_only: headers = conv.produce_headers() - body = conv.convert(unified_job.result_stdout_raw_limited(start_line, end_line), full=False) + body = conv.convert(content, full=False) data = '\n'.join([headers, body]) data = '
%s
' % data else: - data = conv.convert(unified_job.result_stdout_raw_limited(start_line, end_line)) + data = conv.convert(content) # Fix ugly grey background used by default. data = data.replace('.body_background { background-color: #AAAAAA; }', '.body_background { background-color: #f5f5f5; }') + if request.accepted_renderer.format == 'json': + return Response({'range': {'start': start, 'end': end}, 'content': body}) return Response(data) elif request.accepted_renderer.format == 'ansi': return Response(unified_job.result_stdout_raw) diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 152d5910aa..aa9a48f826 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -523,13 +523,24 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique return_buffer = "" if end_line is not None: end_line = int(end_line) - for line in self.result_stdout_raw_handle().readlines()[int(start_line):end_line]: + stdout_lines = self.result_stdout_raw_handle().readlines() + for line in stdout_lines[int(start_line):end_line]: return_buffer += line - return return_buffer + if int(start_line) < 0: + start_actual = len(stdout_lines) + int(start_line) + end_actual = len(stdout_lines) + else: + start_actual = int(start_line) + if end_line is not None: + end_actual = min(int(start_line)+int(end_line), len(stdout_lines)) + else: + end_actual = len(stdout_lines) + return return_buffer, start_actual, end_actual def result_stdout_limited(self, start_line=0, end_line=None): ansi_escape = re.compile(r'\x1b[^m]*m') - return ansi_escape.sub('', self.result_stdout_raw_limited(start_line, end_line)) + content, start, end = self.result_stdout_raw_limited(start_line, end_line) + return ansi_escape.sub('', content), start, end @property def celery_task(self):