Permalink
Browse files

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
  • Loading branch information...
1 parent 0e32899 commit 0065e6642dafe2a43480d1e6280bff1128b33775 @gabrielhurley gabrielhurley committed Nov 18, 2012
View
@@ -39,26 +39,13 @@
from django.utils.translation import ugettext as _
from horizon import loaders
+from horizon import conf
from horizon.decorators import require_auth, require_perms, _current_component
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):
for pattern in urlpatterns:
if getattr(pattern, 'callback', None):
@@ -591,9 +578,7 @@ def __repr__(self):
@property
def _conf(self):
- conf = copy.copy(HORIZON_CONFIG)
- conf.update(getattr(settings, 'HORIZON_CONFIG', {}))
- return conf
+ return conf.HORIZON_CONFIG
@property
def dashboards(self):
@@ -633,11 +618,11 @@ def get_dashboards(self):
""" Returns an ordered tuple of :class:`~horizon.Dashboard` modules.
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.
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.
"""
if self.dashboards:
@@ -660,7 +645,7 @@ def get_dashboards(self):
def get_default_dashboard(self):
""" 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
returned by :func:`~horizon.get_dashboards` will be returned.
"""
@@ -680,7 +665,7 @@ def get_user_home(self, user):
An alternative function can be supplied to customize this behavior
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::
{"user_home": "/home",} # A URL
@@ -741,9 +726,8 @@ def _urls(self):
dash._autodiscover()
# Allow for override modules
- config = getattr(settings, "HORIZON_CONFIG", {})
- if config.get("customization_module", None):
- customization_module = config["customization_module"]
+ if self._conf.get("customization_module", None):
+ customization_module = self._conf["customization_module"]
bits = customization_module.split('.')
mod_name = bits.pop()
package = '.'.join(bits)
View
@@ -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()
View
@@ -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.
"""
-from django.conf import settings
+from horizon import conf
def horizon(request):
@@ -37,7 +37,7 @@ def horizon(request):
for each template/template fragment which takes context that is used
to render the complete output.
"""
- context = {"HORIZON_CONFIG": getattr(settings, "HORIZON_CONFIG", {}),
+ context = {"HORIZON_CONFIG": conf.HORIZON_CONFIG,
"True": True,
"False": False}
View
@@ -22,14 +22,14 @@
import os
import sys
-from django.conf import settings
from django.contrib.auth import logout
from django.http import HttpRequest
from django.utils import termcolors
from django.utils.translation import ugettext as _
from django.views.debug import SafeExceptionReporterFilter, CLEANSED_SUBSTITUTE
from horizon import messages
+from horizon.conf import HORIZON_CONFIG
LOG = logging.getLogger(__name__)
PALETTE = termcolors.PALETTES[termcolors.DEFAULT_PALETTE]
@@ -194,12 +194,10 @@ def __init__(self, wrapped):
self.wrapped = wrapped
-HORIZON_CONFIG = getattr(settings, "HORIZON_CONFIG", {})
-EXCEPTION_CONFIG = HORIZON_CONFIG.get("exceptions", {})
-UNAUTHORIZED = tuple(EXCEPTION_CONFIG.get('unauthorized', []))
-NOT_FOUND = tuple(EXCEPTION_CONFIG.get('not_found', []))
+UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
+NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
RECOVERABLE = (AlreadyExists,)
-RECOVERABLE += tuple(EXCEPTION_CONFIG.get('recoverable', []))
+RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
def error_color(msg):
@@ -2,7 +2,7 @@
*
* Note: The number of concurrent AJAX connections hanlded in the queue
* 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).
*/
horizon.ajax = {
View
@@ -35,6 +35,7 @@
from django.utils.safestring import mark_safe
from django.utils import termcolors
+from horizon import conf
from horizon import exceptions
from horizon import messages
from horizon.utils import html
@@ -359,7 +360,7 @@ class Row(html.HTMLElement):
lookup versus the table's "list" lookup).
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).
.. attribute:: table
@@ -452,7 +453,7 @@ def load_cells(self, datum=None):
self.cells = SortedDict(cells)
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-url'] = self.get_ajax_update_url()
self.classes.append("ajax-update")
@@ -14,12 +14,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from django.conf import settings
from django.core.exceptions import ValidationError
-from django.utils.translation import ugettext as _
-horizon_config = getattr(settings, "HORIZON_CONFIG", {})
-password_config = horizon_config.get("password_validator", {})
+from horizon import conf
def validate_port_range(port):
@@ -28,8 +25,8 @@ def validate_port_range(port):
def password_validator():
- return password_config.get("regex", ".*")
+ return conf.HORIZON_CONFIG["password_validator"]["regex"]
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,
project_id=request.user.tenant_id,
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.management_url = url_for(request, 'compute')
return c
@@ -2,6 +2,8 @@ import os
from django.utils.translation import ugettext_lazy as _
+from openstack_dashboard import exceptions
+
DEBUG = True
TEMPLATE_DEBUG = DEBUG
@@ -12,14 +14,23 @@ TEMPLATE_DEBUG = DEBUG
# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header
# 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.
-# HORIZON_CONFIG = {
-# "password_validator": {
-# "regex": '.*',
-# "help_text": _("Your password does not meet the requirements.")
-# },
-# 'help_url': "http://docs.openstack.org"
-# }
+# HORIZON_CONFIG["password_validator"] = {
+# "regex": '.*',
+# "help_text": _("Your password does not meet the requirements.")
+# },
LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
@@ -16,7 +16,6 @@
ROOT_URLCONF = 'openstack_dashboard.urls'
TEMPLATE_DIRS = (
os.path.join(TEST_DIR, 'templates'),
- #os.path.join(ROOT_PATH, 'templates'),
)
TEMPLATE_CONTEXT_PROCESSORS += (
@@ -25,7 +25,7 @@
def create_stubbed_exception(cls, status_code=500):
msg = "Expected failure."
- def fake_init_exception(self, code, message):
+ def fake_init_exception(self, code, message, **kwargs):
self.code = code
self.message = message

0 comments on commit 0065e66

Please sign in to comment.