mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 07:26:03 -03:30
Add response headers for timing API requests, improve inventory script view performance.
This commit is contained in:
committed by
Matthew Jones
parent
2e0ad0edbf
commit
4f74afdf19
@@ -5,10 +5,13 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.db import connection
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
@@ -75,12 +78,33 @@ class APIView(views.APIView):
|
|||||||
def initialize_request(self, request, *args, **kwargs):
|
def initialize_request(self, request, *args, **kwargs):
|
||||||
'''
|
'''
|
||||||
Store the Django REST Framework Request object as an attribute on the
|
Store the Django REST Framework Request object as an attribute on the
|
||||||
normal Django request.
|
normal Django request, store time the request started.
|
||||||
'''
|
'''
|
||||||
|
self.time_started = time.time()
|
||||||
|
if getattr(settings, 'SQL_DEBUG', False):
|
||||||
|
self.queries_before = len(connection.queries)
|
||||||
drf_request = super(APIView, self).initialize_request(request, *args, **kwargs)
|
drf_request = super(APIView, self).initialize_request(request, *args, **kwargs)
|
||||||
request.drf_request = drf_request
|
request.drf_request = drf_request
|
||||||
return drf_request
|
return drf_request
|
||||||
|
|
||||||
|
def finalize_response(self, request, response, *args, **kwargs):
|
||||||
|
'''
|
||||||
|
Log warning for 400 requests. Add header with elapsed time.
|
||||||
|
'''
|
||||||
|
if response.status_code >= 400:
|
||||||
|
logger.warn("status %s received by user %s attempting to access %s" % (response.status_code, request.user, request.path))
|
||||||
|
response = super(APIView, self).finalize_response(request, response, *args, **kwargs)
|
||||||
|
time_started = getattr(self, 'time_started', None)
|
||||||
|
if time_started:
|
||||||
|
time_elapsed = time.time() - self.time_started
|
||||||
|
response['X-API-Time'] = '%0.3fs' % time_elapsed
|
||||||
|
if getattr(settings, 'SQL_DEBUG', False):
|
||||||
|
queries_before = getattr(self, 'queries_before', 0)
|
||||||
|
q_times = [float(q['time']) for q in connection.queries[queries_before:]]
|
||||||
|
response['X-API-Query-Count'] = len(q_times)
|
||||||
|
response['X-API-Query-Time'] = '%0.3fs' % sum(q_times)
|
||||||
|
return response
|
||||||
|
|
||||||
def get_authenticate_header(self, request):
|
def get_authenticate_header(self, request):
|
||||||
"""
|
"""
|
||||||
Determine the WWW-Authenticate header to use for 401 responses. Try to
|
Determine the WWW-Authenticate header to use for 401 responses. Try to
|
||||||
@@ -134,11 +158,6 @@ class APIView(views.APIView):
|
|||||||
ret['added_in_version'] = added_in_version
|
ret['added_in_version'] = added_in_version
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def finalize_response(self, request, response, *args, **kwargs):
|
|
||||||
if response.status_code >= 400:
|
|
||||||
logger.warn("status %s received by user %s attempting to access %s" % (response.status_code, request.user, request.path))
|
|
||||||
return super(APIView, self).finalize_response(request, response, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class GenericAPIView(generics.GenericAPIView, APIView):
|
class GenericAPIView(generics.GenericAPIView, APIView):
|
||||||
# Base class for all model-based views.
|
# Base class for all model-based views.
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# Copyright (c) 2014 AnsibleWorks, Inc.
|
# Copyright (c) 2014 AnsibleWorks, Inc.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
|
# Django
|
||||||
|
from django.utils.datastructures import SortedDict
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework import renderers
|
from rest_framework import renderers
|
||||||
|
|
||||||
@@ -31,6 +34,15 @@ class BrowsableAPIRenderer(renderers.BrowsableAPIRenderer):
|
|||||||
if method in ('DELETE', 'OPTIONS'):
|
if method in ('DELETE', 'OPTIONS'):
|
||||||
return True # Don't actually need to return a form
|
return True # Don't actually need to return a form
|
||||||
|
|
||||||
|
def get_context(self, data, accepted_media_type, renderer_context):
|
||||||
|
context = super(BrowsableAPIRenderer, self).get_context(data, accepted_media_type, renderer_context)
|
||||||
|
# FIXME: Sort headers / preserve sorting?
|
||||||
|
#response_headers = SortedDict(context['response'].items())
|
||||||
|
#if 'Content-Type' in context['response_headers']:
|
||||||
|
# response_headers['Content-Type'] = context['response_headers']['Content-Type']
|
||||||
|
#context['response_headers'] = response_headers
|
||||||
|
return context
|
||||||
|
|
||||||
class PlainTextRenderer(renderers.BaseRenderer):
|
class PlainTextRenderer(renderers.BaseRenderer):
|
||||||
|
|
||||||
media_type = 'text/plain'
|
media_type = 'text/plain'
|
||||||
|
|||||||
@@ -947,12 +947,36 @@ class InventoryScriptView(RetrieveAPIView):
|
|||||||
data['all'] = SortedDict()
|
data['all'] = SortedDict()
|
||||||
data['all']['vars'] = self.object.variables_dict
|
data['all']['vars'] = self.object.variables_dict
|
||||||
|
|
||||||
|
# Build in-memory mapping of groups and their hosts.
|
||||||
|
group_hosts_kw = dict(group__inventory_id=self.object.id, group__active=True,
|
||||||
|
host__inventory_id=self.object.id, host__active=True)
|
||||||
|
if 'enabled' in hosts_q:
|
||||||
|
group_hosts_kw['host__enabled'] = hosts_q['enabled']
|
||||||
|
group_hosts_qs = Group.hosts.through.objects.filter(**group_hosts_kw)
|
||||||
|
group_hosts_qs = group_hosts_qs.order_by('host__name')
|
||||||
|
group_hosts_qs = group_hosts_qs.values_list('group_id', 'host_id', 'host__name')
|
||||||
|
group_hosts_map = {}
|
||||||
|
for group_id, host_id, host_name in group_hosts_qs:
|
||||||
|
group_hostnames = group_hosts_map.setdefault(group_id, [])
|
||||||
|
group_hostnames.append(host_name)
|
||||||
|
|
||||||
|
# Build in-memory mapping of groups and their children.
|
||||||
|
group_parents_qs = Group.parents.through.objects.filter(
|
||||||
|
from_group__inventory_id=self.object.id, from_group__active=True,
|
||||||
|
to_group__inventory_id=self.object.id, to_group__active=True,
|
||||||
|
)
|
||||||
|
group_parents_qs = group_parents_qs.order_by('from_group__name')
|
||||||
|
group_parents_qs = group_parents_qs.values_list('from_group_id', 'from_group__name', 'to_group_id')
|
||||||
|
group_children_map = {}
|
||||||
|
for from_group_id, from_group_name, to_group_id in group_parents_qs:
|
||||||
|
group_children = group_children_map.setdefault(to_group_id, [])
|
||||||
|
group_children.append(from_group_name)
|
||||||
|
|
||||||
|
# Now use in-memory maps to build up group info.
|
||||||
for group in self.object.groups.filter(active=True):
|
for group in self.object.groups.filter(active=True):
|
||||||
hosts = group.hosts.filter(**hosts_q)
|
|
||||||
children = group.children.filter(active=True)
|
|
||||||
group_info = SortedDict()
|
group_info = SortedDict()
|
||||||
group_info['hosts'] = list(hosts.values_list('name', flat=True))
|
group_info['hosts'] = group_hosts_map.get(group.id, [])
|
||||||
group_info['children'] = list(children.values_list('name', flat=True))
|
group_info['children'] = group_children_map.get(group.id, [])
|
||||||
group_info['vars'] = group.variables_dict
|
group_info['vars'] = group.variables_dict
|
||||||
data[group.name] = group_info
|
data[group.name] = group_info
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user