diff --git a/awx/api/generics.py b/awx/api/generics.py index 2628ce2400..d0f90c8766 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -202,6 +202,7 @@ class GenericAPIView(generics.GenericAPIView, APIView): 'model_verbose_name_plural': unicode(self.model._meta.verbose_name_plural), }) d.update({'serializer_fields': self.get_serializer().metadata()}) + d['settings'] = settings return d def metadata(self, request): diff --git a/awx/api/renderers.py b/awx/api/renderers.py index 5f3eee81db..fd60520db2 100644 --- a/awx/api/renderers.py +++ b/awx/api/renderers.py @@ -45,6 +45,9 @@ class PlainTextRenderer(renderers.BaseRenderer): data = unicode(data) return data.encode(self.charset) +class DownloadTextRenderer(PlainTextRenderer): + format = "txt_download" + class AnsiTextRenderer(PlainTextRenderer): media_type = 'text/plain' diff --git a/awx/api/serializers.py b/awx/api/serializers.py index df507202d5..460447c229 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -519,6 +519,11 @@ class UnifiedJobSerializer(BaseSerializer): ret['elapsed'] = float(ret['elapsed']) return ret + def get_result_stdout(self, obj): + obj_size = obj.result_stdout_size + if obj_size > settings.STDOUT_MAX_BYTES_DISPLAY: + return "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size, settings.STDOUT_MAX_BYTES_DISPLAY) + return obj.result_stdout class UnifiedJobListSerializer(UnifiedJobSerializer): diff --git a/awx/api/templates/api/unified_job_stdout.md b/awx/api/templates/api/unified_job_stdout.md index 70e1e1a993..63f7acea8e 100644 --- a/awx/api/templates/api/unified_job_stdout.md +++ b/awx/api/templates/api/unified_job_stdout.md @@ -12,6 +12,7 @@ Use the `format` query string parameter to specify the output format. * Plain Text: `?format=txt` * Plain Text with ANSI color codes: `?format=ansi` * JSON structure: `?format=json` +* Downloaded Plain Text: `?format=txt_download` (_New in Ansible Tower 2.0.0_) When using the Browsable API, HTML and JSON formats, the `start_line` and `end_line` query string parameters can be used @@ -20,4 +21,7 @@ to specify a range of line numbers to retrieve. Use `dark=1` or `dark=0` as a query string parameter to force or disable a dark background. ++Files over {{ settings.STDOUT_MAX_BYTES_DISPLAY|filesizeformat }} (configurable) will not display in the browser. Use the `txt_download` ++format to download the file directly to view it. + {% include "api/_new_in_awx.md" %} diff --git a/awx/api/views.py b/awx/api/views.py index d47d89e789..a0d383d758 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -25,6 +25,8 @@ from django.utils.safestring import mark_safe from django.utils.timezone import now from django.views.decorators.csrf import csrf_exempt from django.template.loader import render_to_string +from django.core.servers.basehttp import FileWrapper +from django.http import HttpResponse # Django REST Framework from rest_framework.exceptions import PermissionDenied, ParseError @@ -2789,7 +2791,7 @@ class UnifiedJobStdout(RetrieveAPIView): serializer_class = UnifiedJobStdoutSerializer renderer_classes = [BrowsableAPIRenderer, renderers.StaticHTMLRenderer, PlainTextRenderer, AnsiTextRenderer, - renderers.JSONRenderer] + renderers.JSONRenderer, DownloadTextRenderer] filter_backends = () new_in_148 = True diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index dcfdebe2a2..5caaaf06d3 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -115,6 +115,8 @@ ALLOWED_HOSTS = [] # reverse proxy. REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST'] +STDOUT_MAX_BYTES_DISPLAY = 1048576 + TEMPLATE_CONTEXT_PROCESSORS += ( # NOQA 'django.core.context_processors.request', 'awx.ui.context_processors.settings',