Unifies Horizon conf.
Centralizes all of Horizon's configuration options so that they're all uniformly accesible from a single place and always guaranteed to exist. Implements blueprint unify-config. Change-Id: I3279b7ccd58302fcff4f0d273f89f282a285c442
This commit is contained in:
parent
0e328995ec
commit
0065e6642d
@ -39,26 +39,13 @@ from django.utils.module_loading import module_has_submodule
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from horizon import loaders
|
from horizon import loaders
|
||||||
|
from horizon import conf
|
||||||
from horizon.decorators import require_auth, require_perms, _current_component
|
from horizon.decorators import require_auth, require_perms, _current_component
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# Default configuration dictionary. Do not mutate directly. Use copy.copy().
|
|
||||||
HORIZON_CONFIG = {
|
|
||||||
# Allow for ordering dashboards; list or tuple if provided.
|
|
||||||
'dashboards': None,
|
|
||||||
# Name of a default dashboard; defaults to first alphabetically if None
|
|
||||||
'default_dashboard': None,
|
|
||||||
# Default redirect url for users' home
|
|
||||||
'user_home': settings.LOGIN_REDIRECT_URL,
|
|
||||||
'exceptions': {'unauthorized': [],
|
|
||||||
'not_found': [],
|
|
||||||
'recoverable': []}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs):
|
def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs):
|
||||||
for pattern in urlpatterns:
|
for pattern in urlpatterns:
|
||||||
if getattr(pattern, 'callback', None):
|
if getattr(pattern, 'callback', None):
|
||||||
@ -591,9 +578,7 @@ class Site(Registry, HorizonComponent):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def _conf(self):
|
def _conf(self):
|
||||||
conf = copy.copy(HORIZON_CONFIG)
|
return conf.HORIZON_CONFIG
|
||||||
conf.update(getattr(settings, 'HORIZON_CONFIG', {}))
|
|
||||||
return conf
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dashboards(self):
|
def dashboards(self):
|
||||||
@ -633,11 +618,11 @@ class Site(Registry, HorizonComponent):
|
|||||||
""" Returns an ordered tuple of :class:`~horizon.Dashboard` modules.
|
""" Returns an ordered tuple of :class:`~horizon.Dashboard` modules.
|
||||||
|
|
||||||
Orders dashboards according to the ``"dashboards"`` key in
|
Orders dashboards according to the ``"dashboards"`` key in
|
||||||
``settings.HORIZON_CONFIG`` or else returns all registered dashboards
|
``HORIZON_CONFIG`` or else returns all registered dashboards
|
||||||
in alphabetical order.
|
in alphabetical order.
|
||||||
|
|
||||||
Any remaining :class:`~horizon.Dashboard` classes registered with
|
Any remaining :class:`~horizon.Dashboard` classes registered with
|
||||||
Horizon but not listed in ``settings.HORIZON_CONFIG['dashboards']``
|
Horizon but not listed in ``HORIZON_CONFIG['dashboards']``
|
||||||
will be appended to the end of the list alphabetically.
|
will be appended to the end of the list alphabetically.
|
||||||
"""
|
"""
|
||||||
if self.dashboards:
|
if self.dashboards:
|
||||||
@ -660,7 +645,7 @@ class Site(Registry, HorizonComponent):
|
|||||||
def get_default_dashboard(self):
|
def get_default_dashboard(self):
|
||||||
""" Returns the default :class:`~horizon.Dashboard` instance.
|
""" Returns the default :class:`~horizon.Dashboard` instance.
|
||||||
|
|
||||||
If ``"default_dashboard"`` is specified in ``settings.HORIZON_CONFIG``
|
If ``"default_dashboard"`` is specified in ``HORIZON_CONFIG``
|
||||||
then that dashboard will be returned. If not, the first dashboard
|
then that dashboard will be returned. If not, the first dashboard
|
||||||
returned by :func:`~horizon.get_dashboards` will be returned.
|
returned by :func:`~horizon.get_dashboards` will be returned.
|
||||||
"""
|
"""
|
||||||
@ -680,7 +665,7 @@ class Site(Registry, HorizonComponent):
|
|||||||
|
|
||||||
An alternative function can be supplied to customize this behavior
|
An alternative function can be supplied to customize this behavior
|
||||||
by specifying a either a URL or a function which returns a URL via
|
by specifying a either a URL or a function which returns a URL via
|
||||||
the ``"user_home"`` key in ``settings.HORIZON_CONFIG``. Each of these
|
the ``"user_home"`` key in ``HORIZON_CONFIG``. Each of these
|
||||||
would be valid::
|
would be valid::
|
||||||
|
|
||||||
{"user_home": "/home",} # A URL
|
{"user_home": "/home",} # A URL
|
||||||
@ -741,9 +726,8 @@ class Site(Registry, HorizonComponent):
|
|||||||
dash._autodiscover()
|
dash._autodiscover()
|
||||||
|
|
||||||
# Allow for override modules
|
# Allow for override modules
|
||||||
config = getattr(settings, "HORIZON_CONFIG", {})
|
if self._conf.get("customization_module", None):
|
||||||
if config.get("customization_module", None):
|
customization_module = self._conf["customization_module"]
|
||||||
customization_module = config["customization_module"]
|
|
||||||
bits = customization_module.split('.')
|
bits = customization_module.split('.')
|
||||||
mod_name = bits.pop()
|
mod_name = bits.pop()
|
||||||
package = '.'.join(bits)
|
package = '.'.join(bits)
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import copy
|
||||||
|
|
||||||
|
from django.utils.functional import LazyObject, empty
|
||||||
|
|
||||||
|
from .default import HORIZON_CONFIG as DEFAULT_CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
class LazySettings(LazyObject):
|
||||||
|
def _setup(self, name=None):
|
||||||
|
from django.conf import settings
|
||||||
|
HORIZON_CONFIG = copy.copy(DEFAULT_CONFIG)
|
||||||
|
HORIZON_CONFIG.update(settings.HORIZON_CONFIG)
|
||||||
|
|
||||||
|
# Ensure we always have our exception configuration...
|
||||||
|
for exc_category in ['unauthorized', 'not_found', 'recoverable']:
|
||||||
|
if exc_category not in HORIZON_CONFIG['exceptions']:
|
||||||
|
default_exc_config = DEFAULT_CONFIG['exceptions'][exc_category]
|
||||||
|
HORIZON_CONFIG['exceptions'][exc_category] = default_exc_config
|
||||||
|
|
||||||
|
# Ensure our password validator always exists...
|
||||||
|
if 'regex' not in HORIZON_CONFIG['password_validator']:
|
||||||
|
default_pw_regex = DEFAULT_CONFIG['password_validator']['regex']
|
||||||
|
HORIZON_CONFIG['password_validator']['regex'] = default_pw_regex
|
||||||
|
if 'help_text' not in HORIZON_CONFIG['password_validator']:
|
||||||
|
default_pw_help = DEFAULT_CONFIG['password_validator']['help_text']
|
||||||
|
HORIZON_CONFIG['password_validator']['help_text'] = default_pw_help
|
||||||
|
|
||||||
|
self._wrapped = HORIZON_CONFIG
|
||||||
|
|
||||||
|
def __getitem__(self, name, fallback=None):
|
||||||
|
if self._wrapped is empty:
|
||||||
|
self._setup(name)
|
||||||
|
return self._wrapped.get(name, fallback)
|
||||||
|
|
||||||
|
HORIZON_CONFIG = LazySettings()
|
30
horizon/conf/default.py
Normal file
30
horizon/conf/default.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
# Default configuration dictionary. Do not mutate.
|
||||||
|
HORIZON_CONFIG = {
|
||||||
|
# Allow for ordering dashboards; list or tuple if provided.
|
||||||
|
'dashboards': None,
|
||||||
|
|
||||||
|
# Name of a default dashboard; defaults to first alphabetically if None
|
||||||
|
'default_dashboard': None,
|
||||||
|
|
||||||
|
# Default redirect url for users' home
|
||||||
|
'user_home': settings.LOGIN_REDIRECT_URL,
|
||||||
|
|
||||||
|
# AJAX settings for JavaScript
|
||||||
|
'ajax_queue_limit': 10,
|
||||||
|
'ajax_poll_interval': 2500,
|
||||||
|
|
||||||
|
# URL for additional help with this site.
|
||||||
|
'help_url': None,
|
||||||
|
|
||||||
|
# Exception configuration.
|
||||||
|
'exceptions': {'unauthorized': [],
|
||||||
|
'not_found': [],
|
||||||
|
'recoverable': []},
|
||||||
|
|
||||||
|
# Password configuration.
|
||||||
|
'password_validator': {'regex': '.*',
|
||||||
|
'help_text': _("Password is not accepted")}
|
||||||
|
}
|
@ -21,7 +21,7 @@
|
|||||||
Context processors used by Horizon.
|
Context processors used by Horizon.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from horizon import conf
|
||||||
|
|
||||||
|
|
||||||
def horizon(request):
|
def horizon(request):
|
||||||
@ -37,7 +37,7 @@ def horizon(request):
|
|||||||
for each template/template fragment which takes context that is used
|
for each template/template fragment which takes context that is used
|
||||||
to render the complete output.
|
to render the complete output.
|
||||||
"""
|
"""
|
||||||
context = {"HORIZON_CONFIG": getattr(settings, "HORIZON_CONFIG", {}),
|
context = {"HORIZON_CONFIG": conf.HORIZON_CONFIG,
|
||||||
"True": True,
|
"True": True,
|
||||||
"False": False}
|
"False": False}
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth import logout
|
from django.contrib.auth import logout
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.utils import termcolors
|
from django.utils import termcolors
|
||||||
@ -30,6 +29,7 @@ from django.utils.translation import ugettext as _
|
|||||||
from django.views.debug import SafeExceptionReporterFilter, CLEANSED_SUBSTITUTE
|
from django.views.debug import SafeExceptionReporterFilter, CLEANSED_SUBSTITUTE
|
||||||
|
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
|
from horizon.conf import HORIZON_CONFIG
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
PALETTE = termcolors.PALETTES[termcolors.DEFAULT_PALETTE]
|
PALETTE = termcolors.PALETTES[termcolors.DEFAULT_PALETTE]
|
||||||
@ -194,12 +194,10 @@ class HandledException(HorizonException):
|
|||||||
self.wrapped = wrapped
|
self.wrapped = wrapped
|
||||||
|
|
||||||
|
|
||||||
HORIZON_CONFIG = getattr(settings, "HORIZON_CONFIG", {})
|
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
|
||||||
EXCEPTION_CONFIG = HORIZON_CONFIG.get("exceptions", {})
|
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
|
||||||
UNAUTHORIZED = tuple(EXCEPTION_CONFIG.get('unauthorized', []))
|
|
||||||
NOT_FOUND = tuple(EXCEPTION_CONFIG.get('not_found', []))
|
|
||||||
RECOVERABLE = (AlreadyExists,)
|
RECOVERABLE = (AlreadyExists,)
|
||||||
RECOVERABLE += tuple(EXCEPTION_CONFIG.get('recoverable', []))
|
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
|
||||||
|
|
||||||
|
|
||||||
def error_color(msg):
|
def error_color(msg):
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Note: The number of concurrent AJAX connections hanlded in the queue
|
* Note: The number of concurrent AJAX connections hanlded in the queue
|
||||||
* can be configured by setting an "ajax_queue_limit" key in
|
* can be configured by setting an "ajax_queue_limit" key in
|
||||||
* settings.HORIZON_CONFIG to the desired number (or None to disable queue
|
* HORIZON_CONFIG to the desired number (or None to disable queue
|
||||||
* limiting).
|
* limiting).
|
||||||
*/
|
*/
|
||||||
horizon.ajax = {
|
horizon.ajax = {
|
||||||
|
@ -35,6 +35,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils import termcolors
|
from django.utils import termcolors
|
||||||
|
|
||||||
|
from horizon import conf
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
from horizon.utils import html
|
from horizon.utils import html
|
||||||
@ -359,7 +360,7 @@ class Row(html.HTMLElement):
|
|||||||
lookup versus the table's "list" lookup).
|
lookup versus the table's "list" lookup).
|
||||||
|
|
||||||
The automatic update interval is configurable by setting the key
|
The automatic update interval is configurable by setting the key
|
||||||
``ajax_poll_interval`` in the ``settings.HORIZON_CONFIG`` dictionary.
|
``ajax_poll_interval`` in the ``HORIZON_CONFIG`` dictionary.
|
||||||
Default: ``2500`` (measured in milliseconds).
|
Default: ``2500`` (measured in milliseconds).
|
||||||
|
|
||||||
.. attribute:: table
|
.. attribute:: table
|
||||||
@ -452,7 +453,7 @@ class Row(html.HTMLElement):
|
|||||||
self.cells = SortedDict(cells)
|
self.cells = SortedDict(cells)
|
||||||
|
|
||||||
if self.ajax:
|
if self.ajax:
|
||||||
interval = settings.HORIZON_CONFIG.get('ajax_poll_interval', 2500)
|
interval = conf.HORIZON_CONFIG['ajax_poll_interval']
|
||||||
self.attrs['data-update-interval'] = interval
|
self.attrs['data-update-interval'] = interval
|
||||||
self.attrs['data-update-url'] = self.get_ajax_update_url()
|
self.attrs['data-update-url'] = self.get_ajax_update_url()
|
||||||
self.classes.append("ajax-update")
|
self.classes.append("ajax-update")
|
||||||
|
@ -14,12 +14,9 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
horizon_config = getattr(settings, "HORIZON_CONFIG", {})
|
from horizon import conf
|
||||||
password_config = horizon_config.get("password_validator", {})
|
|
||||||
|
|
||||||
|
|
||||||
def validate_port_range(port):
|
def validate_port_range(port):
|
||||||
@ -28,8 +25,8 @@ def validate_port_range(port):
|
|||||||
|
|
||||||
|
|
||||||
def password_validator():
|
def password_validator():
|
||||||
return password_config.get("regex", ".*")
|
return conf.HORIZON_CONFIG["password_validator"]["regex"]
|
||||||
|
|
||||||
|
|
||||||
def password_validator_msg():
|
def password_validator_msg():
|
||||||
return password_config.get("help_text", _("Password is not accepted"))
|
return conf.HORIZON_CONFIG["password_validator"]["help_text"]
|
||||||
|
@ -183,7 +183,8 @@ def novaclient(request):
|
|||||||
request.user.token.id,
|
request.user.token.id,
|
||||||
project_id=request.user.tenant_id,
|
project_id=request.user.tenant_id,
|
||||||
auth_url=url_for(request, 'compute'),
|
auth_url=url_for(request, 'compute'),
|
||||||
insecure=insecure)
|
insecure=insecure,
|
||||||
|
http_log_debug=settings.DEBUG)
|
||||||
c.client.auth_token = request.user.token.id
|
c.client.auth_token = request.user.token.id
|
||||||
c.client.management_url = url_for(request, 'compute')
|
c.client.management_url = url_for(request, 'compute')
|
||||||
return c
|
return c
|
||||||
|
@ -2,6 +2,8 @@ import os
|
|||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from openstack_dashboard import exceptions
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
TEMPLATE_DEBUG = DEBUG
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
@ -12,14 +14,23 @@ TEMPLATE_DEBUG = DEBUG
|
|||||||
# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header
|
# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header
|
||||||
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
|
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
|
||||||
|
|
||||||
|
# Default OpenStack Dashboard configuration.
|
||||||
|
HORIZON_CONFIG = {
|
||||||
|
'dashboards': ('project', 'admin', 'settings',),
|
||||||
|
'default_dashboard': 'project',
|
||||||
|
'user_home': 'openstack_dashboard.views.get_user_home',
|
||||||
|
'ajax_queue_limit': 10,
|
||||||
|
'help_url': "http://docs.openstack.org",
|
||||||
|
'exceptions': {'recoverable': exceptions.RECOVERABLE,
|
||||||
|
'not_found': exceptions.NOT_FOUND,
|
||||||
|
'unauthorized': exceptions.UNAUTHORIZED},
|
||||||
|
}
|
||||||
|
|
||||||
# Specify a regular expression to validate user passwords.
|
# Specify a regular expression to validate user passwords.
|
||||||
# HORIZON_CONFIG = {
|
# HORIZON_CONFIG["password_validator"] = {
|
||||||
# "password_validator": {
|
# "regex": '.*',
|
||||||
# "regex": '.*',
|
# "help_text": _("Your password does not meet the requirements.")
|
||||||
# "help_text": _("Your password does not meet the requirements.")
|
# },
|
||||||
# },
|
|
||||||
# 'help_url': "http://docs.openstack.org"
|
|
||||||
# }
|
|
||||||
|
|
||||||
LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ SECRET_KEY = generate_or_read_from_file(os.path.join(TEST_DIR,
|
|||||||
ROOT_URLCONF = 'openstack_dashboard.urls'
|
ROOT_URLCONF = 'openstack_dashboard.urls'
|
||||||
TEMPLATE_DIRS = (
|
TEMPLATE_DIRS = (
|
||||||
os.path.join(TEST_DIR, 'templates'),
|
os.path.join(TEST_DIR, 'templates'),
|
||||||
#os.path.join(ROOT_PATH, 'templates'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS += (
|
TEMPLATE_CONTEXT_PROCESSORS += (
|
||||||
|
@ -25,7 +25,7 @@ from .utils import TestDataContainer
|
|||||||
def create_stubbed_exception(cls, status_code=500):
|
def create_stubbed_exception(cls, status_code=500):
|
||||||
msg = "Expected failure."
|
msg = "Expected failure."
|
||||||
|
|
||||||
def fake_init_exception(self, code, message):
|
def fake_init_exception(self, code, message, **kwargs):
|
||||||
self.code = code
|
self.code = code
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user