mirror of
https://github.com/ansible/awx.git
synced 2026-03-10 22:19:28 -02:30
Merge pull request #1018 from cchurch/redis-cache
Use Redis as cache backend.
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
|
|
||||||
from awx.main.task_engine import TaskSerializer
|
from awx.main.task_engine import TaskSerializer
|
||||||
|
from awx.main.utils import memoize
|
||||||
|
|
||||||
|
|
||||||
class LicenseForbids(APIException):
|
class LicenseForbids(APIException):
|
||||||
@@ -11,6 +12,7 @@ class LicenseForbids(APIException):
|
|||||||
default_detail = 'Your Tower license does not allow that.'
|
default_detail = 'Your Tower license does not allow that.'
|
||||||
|
|
||||||
|
|
||||||
|
@memoize()
|
||||||
def get_license(show_key=False, bypass_database=False):
|
def get_license(show_key=False, bypass_database=False):
|
||||||
"""Return a dictionary representing the license currently in
|
"""Return a dictionary representing the license currently in
|
||||||
place on this Tower instance.
|
place on this Tower instance.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from collections import OrderedDict
|
|||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.cache import cache
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
@@ -263,6 +264,8 @@ class ApiV1ConfigView(APIView):
|
|||||||
if license_data['valid_key']:
|
if license_data['valid_key']:
|
||||||
tower_settings.LICENSE = data_actual
|
tower_settings.LICENSE = data_actual
|
||||||
tower_settings.TOWER_URL_BASE = "{}://{}".format(request.scheme, request.get_host())
|
tower_settings.TOWER_URL_BASE = "{}://{}".format(request.scheme, request.get_host())
|
||||||
|
# Clear cache when license is updated.
|
||||||
|
cache.clear()
|
||||||
return Response(license_data)
|
return Response(license_data)
|
||||||
|
|
||||||
return Response({"error": "Invalid license"}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"error": "Invalid license"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
@@ -282,6 +285,8 @@ class ApiV1ConfigView(APIView):
|
|||||||
break
|
break
|
||||||
|
|
||||||
TowerSettings.objects.filter(key="LICENSE").delete()
|
TowerSettings.objects.filter(key="LICENSE").delete()
|
||||||
|
# Clear cache when license is updated.
|
||||||
|
cache.clear()
|
||||||
|
|
||||||
# Only stop mongod if license removal succeeded
|
# Only stop mongod if license removal succeeded
|
||||||
if has_error is None:
|
if has_error is None:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import yaml
|
|||||||
import django.test
|
import django.test
|
||||||
from django.conf import settings, UserSettingsHolder
|
from django.conf import settings, UserSettingsHolder
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.cache import cache
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
@@ -152,6 +153,7 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin):
|
|||||||
'LOCATION': 'unittests'
|
'LOCATION': 'unittests'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cache.clear()
|
||||||
self._start_time = time.time()
|
self._start_time = time.time()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@@ -195,6 +197,7 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin):
|
|||||||
writer.write_file(license_path)
|
writer.write_file(license_path)
|
||||||
self._temp_paths.append(license_path)
|
self._temp_paths.append(license_path)
|
||||||
os.environ['AWX_LICENSE_FILE'] = license_path
|
os.environ['AWX_LICENSE_FILE'] = license_path
|
||||||
|
cache.clear()
|
||||||
|
|
||||||
def create_basic_license_file(self, instance_count=100, license_date=int(time.time() + 3600)):
|
def create_basic_license_file(self, instance_count=100, license_date=int(time.time() + 3600)):
|
||||||
writer = LicenseWriter(
|
writer = LicenseWriter(
|
||||||
@@ -209,6 +212,7 @@ class BaseTestMixin(QueueTestMixin, MockCommonlySlowTestMixin):
|
|||||||
writer.write_file(license_path)
|
writer.write_file(license_path)
|
||||||
self._temp_paths.append(license_path)
|
self._temp_paths.append(license_path)
|
||||||
os.environ['AWX_LICENSE_FILE'] = license_path
|
os.environ['AWX_LICENSE_FILE'] = license_path
|
||||||
|
cache.clear()
|
||||||
|
|
||||||
def create_expired_license_file(self, instance_count=1000, grace_period=False):
|
def create_expired_license_file(self, instance_count=1000, grace_period=False):
|
||||||
license_date = time.time() - 1
|
license_date = time.time() - 1
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ import threading
|
|||||||
import contextlib
|
import contextlib
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
# Decorator
|
||||||
|
from decorator import decorator
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework.exceptions import ParseError, PermissionDenied
|
from rest_framework.exceptions import ParseError, PermissionDenied
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
|
from django.utils.text import slugify
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
|
||||||
@@ -27,7 +31,7 @@ from Crypto.Cipher import AES
|
|||||||
|
|
||||||
logger = logging.getLogger('awx.main.utils')
|
logger = logging.getLogger('awx.main.utils')
|
||||||
|
|
||||||
__all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore',
|
__all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'memoize',
|
||||||
'get_ansible_version', 'get_ssh_version', 'get_awx_version', 'update_scm_url',
|
'get_ansible_version', 'get_ssh_version', 'get_awx_version', 'update_scm_url',
|
||||||
'get_type_for_model', 'get_model_for_type', 'to_python_boolean',
|
'get_type_for_model', 'get_model_for_type', 'to_python_boolean',
|
||||||
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal',
|
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal',
|
||||||
@@ -93,6 +97,23 @@ class RequireDebugTrueOrTest(logging.Filter):
|
|||||||
return settings.DEBUG or 'test' in sys.argv
|
return settings.DEBUG or 'test' in sys.argv
|
||||||
|
|
||||||
|
|
||||||
|
def memoize(ttl=60):
|
||||||
|
'''
|
||||||
|
Decorator to wrap a function and cache its result.
|
||||||
|
'''
|
||||||
|
from django.core.cache import cache
|
||||||
|
|
||||||
|
def _memoizer(f, *args, **kwargs):
|
||||||
|
key = slugify('%s %r %r' % (f.__name__, args, kwargs))
|
||||||
|
value = cache.get(key)
|
||||||
|
if value is None:
|
||||||
|
value = f(*args, **kwargs)
|
||||||
|
cache.set(key, value, ttl)
|
||||||
|
return value
|
||||||
|
return decorator(_memoizer)
|
||||||
|
|
||||||
|
|
||||||
|
@memoize()
|
||||||
def get_ansible_version():
|
def get_ansible_version():
|
||||||
'''
|
'''
|
||||||
Return Ansible version installed.
|
Return Ansible version installed.
|
||||||
@@ -101,11 +122,11 @@ def get_ansible_version():
|
|||||||
proc = subprocess.Popen(['ansible', '--version'],
|
proc = subprocess.Popen(['ansible', '--version'],
|
||||||
stdout=subprocess.PIPE)
|
stdout=subprocess.PIPE)
|
||||||
result = proc.communicate()[0]
|
result = proc.communicate()[0]
|
||||||
stripped_result = result.split('\n')[0].replace('ansible', '').strip()
|
return result.split('\n')[0].replace('ansible', '').strip()
|
||||||
return stripped_result
|
|
||||||
except:
|
except:
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
|
||||||
|
@memoize()
|
||||||
def get_ssh_version():
|
def get_ssh_version():
|
||||||
'''
|
'''
|
||||||
Return SSH version installed.
|
Return SSH version installed.
|
||||||
@@ -444,6 +465,7 @@ def ignore_inventory_group_removal():
|
|||||||
finally:
|
finally:
|
||||||
_inventory_updates.is_removing = previous_value
|
_inventory_updates.is_removing = previous_value
|
||||||
|
|
||||||
|
@memoize()
|
||||||
def check_proot_installed():
|
def check_proot_installed():
|
||||||
'''
|
'''
|
||||||
Check that proot is installed.
|
Check that proot is installed.
|
||||||
|
|||||||
@@ -351,6 +351,21 @@ CELERYBEAT_SCHEDULE = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Use Redis as cache backend (except when testing).
|
||||||
|
if is_testing():
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'redis_cache.RedisCache',
|
||||||
|
'LOCATION': BROKER_URL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
# Social Auth configuration.
|
# Social Auth configuration.
|
||||||
SOCIAL_AUTH_STRATEGY = 'social.strategies.django_strategy.DjangoStrategy'
|
SOCIAL_AUTH_STRATEGY = 'social.strategies.django_strategy.DjangoStrategy'
|
||||||
SOCIAL_AUTH_STORAGE = 'social.apps.django_app.default.models.DjangoStorage'
|
SOCIAL_AUTH_STORAGE = 'social.apps.django_app.default.models.DjangoStorage'
|
||||||
|
|||||||
@@ -32,3 +32,7 @@ if not all([SOCIAL_AUTH_SAML_SP_ENTITY_ID, SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
|
|||||||
|
|
||||||
if not AUTH_BASIC_ENABLED:
|
if not AUTH_BASIC_ENABLED:
|
||||||
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = [x for x in REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] if x != 'rest_framework.authentication.BasicAuthentication']
|
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = [x for x in REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] if x != 'rest_framework.authentication.BasicAuthentication']
|
||||||
|
|
||||||
|
# Update cache to use celery broker URL defined in configuration files.
|
||||||
|
if CACHES['default']['BACKEND'] == 'redis_cache.RedisCache':
|
||||||
|
CACHES['default']['LOCATION'] = BROKER_URL
|
||||||
|
|||||||
24
docs/licenses/django-redis-cache.txt
Normal file
24
docs/licenses/django-redis-cache.txt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2015 Sean Bleier
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -21,6 +21,7 @@ django-extensions==1.5.9
|
|||||||
git+https://github.com/chrismeyersfsu/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield
|
git+https://github.com/chrismeyersfsu/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield
|
||||||
django-polymorphic==0.7.2
|
django-polymorphic==0.7.2
|
||||||
django-radius==1.0.0
|
django-radius==1.0.0
|
||||||
|
django-redis-cache==1.6.5
|
||||||
djangorestframework==3.3.2
|
djangorestframework==3.3.2
|
||||||
djangorestframework-yaml==1.0.2
|
djangorestframework-yaml==1.0.2
|
||||||
django-split-settings==0.1.1
|
django-split-settings==0.1.1
|
||||||
|
|||||||
Reference in New Issue
Block a user