Gate on H4xx docstrings for pep8
In an effort to help horizon more friendly to OpenStack hacking guidelines, we now gate on H40* violations. Change-Id: Id07294543660368d2f7f5ac363710176ab23b874 Signed-off-by: Paul Belanger <paul.belanger@polybeacon.com>
This commit is contained in:
parent
94129baebb
commit
da8c69afa6
|
@ -159,7 +159,7 @@ class Registry(object):
|
||||||
|
|
||||||
|
|
||||||
class Panel(HorizonComponent):
|
class Panel(HorizonComponent):
|
||||||
""" A base class for defining Horizon dashboard panels.
|
"""A base class for defining Horizon dashboard panels.
|
||||||
|
|
||||||
All Horizon dashboard panels should extend from this class. It provides
|
All Horizon dashboard panels should extend from this class. It provides
|
||||||
the appropriate hooks for automatically constructing URLconfs, and
|
the appropriate hooks for automatically constructing URLconfs, and
|
||||||
|
@ -214,7 +214,7 @@ class Panel(HorizonComponent):
|
||||||
return "<Panel: %s>" % self.slug
|
return "<Panel: %s>" % self.slug
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
""" Returns the default URL for this panel.
|
"""Returns the default URL for this panel.
|
||||||
|
|
||||||
The default URL is defined as the URL pattern with ``name="index"`` in
|
The default URL is defined as the URL pattern with ``name="index"`` in
|
||||||
the URLconf for this panel.
|
the URLconf for this panel.
|
||||||
|
@ -243,7 +243,7 @@ class Panel(HorizonComponent):
|
||||||
|
|
||||||
|
|
||||||
class PanelGroup(object):
|
class PanelGroup(object):
|
||||||
""" A container for a set of :class:`~horizon.Panel` classes.
|
"""A container for a set of :class:`~horizon.Panel` classes.
|
||||||
|
|
||||||
When iterated, it will yield each of the ``Panel`` instances it
|
When iterated, it will yield each of the ``Panel`` instances it
|
||||||
contains.
|
contains.
|
||||||
|
@ -286,7 +286,7 @@ class PanelGroup(object):
|
||||||
|
|
||||||
|
|
||||||
class Dashboard(Registry, HorizonComponent):
|
class Dashboard(Registry, HorizonComponent):
|
||||||
""" A base class for defining Horizon dashboards.
|
"""A base class for defining Horizon dashboards.
|
||||||
|
|
||||||
All Horizon dashboards should extend from this base class. It provides the
|
All Horizon dashboards should extend from this base class. It provides the
|
||||||
appropriate hooks for automatic discovery of :class:`~horizon.Panel`
|
appropriate hooks for automatic discovery of :class:`~horizon.Panel`
|
||||||
|
@ -390,15 +390,13 @@ class Dashboard(Registry, HorizonComponent):
|
||||||
self._panel_groups = None
|
self._panel_groups = None
|
||||||
|
|
||||||
def get_panel(self, panel):
|
def get_panel(self, panel):
|
||||||
"""
|
"""Returns the specified :class:`~horizon.Panel` instance registered
|
||||||
Returns the specified :class:`~horizon.Panel` instance registered
|
|
||||||
with this dashboard.
|
with this dashboard.
|
||||||
"""
|
"""
|
||||||
return self._registered(panel)
|
return self._registered(panel)
|
||||||
|
|
||||||
def get_panels(self):
|
def get_panels(self):
|
||||||
"""
|
"""Returns the :class:`~horizon.Panel` instances registered with this
|
||||||
Returns the :class:`~horizon.Panel` instances registered with this
|
|
||||||
dashboard in order, without any panel groupings.
|
dashboard in order, without any panel groupings.
|
||||||
"""
|
"""
|
||||||
all_panels = []
|
all_panels = []
|
||||||
|
@ -432,7 +430,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||||
return SortedDict(panel_groups)
|
return SortedDict(panel_groups)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
""" Returns the default URL for this dashboard.
|
"""Returns the default URL for this dashboard.
|
||||||
|
|
||||||
The default URL is defined as the URL pattern with ``name="index"``
|
The default URL is defined as the URL pattern with ``name="index"``
|
||||||
in the URLconf for the :class:`~horizon.Panel` specified by
|
in the URLconf for the :class:`~horizon.Panel` specified by
|
||||||
|
@ -478,7 +476,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||||
return urlpatterns, self.slug, self.slug
|
return urlpatterns, self.slug, self.slug
|
||||||
|
|
||||||
def _autodiscover(self):
|
def _autodiscover(self):
|
||||||
""" Discovers panels to register from the current dashboard module. """
|
"""Discovers panels to register from the current dashboard module."""
|
||||||
if getattr(self, "_autodiscover_complete", False):
|
if getattr(self, "_autodiscover_complete", False):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -520,7 +518,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls, panel):
|
def register(cls, panel):
|
||||||
""" Registers a :class:`~horizon.Panel` with this dashboard. """
|
"""Registers a :class:`~horizon.Panel` with this dashboard."""
|
||||||
panel_class = Horizon.register_panel(cls, panel)
|
panel_class = Horizon.register_panel(cls, panel)
|
||||||
# Support template loading from panel template directories.
|
# Support template loading from panel template directories.
|
||||||
panel_mod = import_module(panel.__module__)
|
panel_mod = import_module(panel.__module__)
|
||||||
|
@ -533,7 +531,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unregister(cls, panel):
|
def unregister(cls, panel):
|
||||||
""" Unregisters a :class:`~horizon.Panel` from this dashboard. """
|
"""Unregisters a :class:`~horizon.Panel` from this dashboard."""
|
||||||
success = Horizon.unregister_panel(cls, panel)
|
success = Horizon.unregister_panel(cls, panel)
|
||||||
if success:
|
if success:
|
||||||
# Remove the panel's template directory.
|
# Remove the panel's template directory.
|
||||||
|
@ -578,7 +576,7 @@ class LazyURLPattern(SimpleLazyObject):
|
||||||
|
|
||||||
|
|
||||||
class Site(Registry, HorizonComponent):
|
class Site(Registry, HorizonComponent):
|
||||||
""" The overarching class which encompasses all dashboards and panels. """
|
"""The overarching class which encompasses all dashboards and panels."""
|
||||||
|
|
||||||
# Required for registry
|
# Required for registry
|
||||||
_registerable_class = Dashboard
|
_registerable_class = Dashboard
|
||||||
|
@ -604,11 +602,11 @@ class Site(Registry, HorizonComponent):
|
||||||
return self._conf['default_dashboard']
|
return self._conf['default_dashboard']
|
||||||
|
|
||||||
def register(self, dashboard):
|
def register(self, dashboard):
|
||||||
""" Registers a :class:`~horizon.Dashboard` with Horizon."""
|
"""Registers a :class:`~horizon.Dashboard` with Horizon."""
|
||||||
return self._register(dashboard)
|
return self._register(dashboard)
|
||||||
|
|
||||||
def unregister(self, dashboard):
|
def unregister(self, dashboard):
|
||||||
""" Unregisters a :class:`~horizon.Dashboard` from Horizon. """
|
"""Unregisters a :class:`~horizon.Dashboard` from Horizon."""
|
||||||
return self._unregister(dashboard)
|
return self._unregister(dashboard)
|
||||||
|
|
||||||
def registered(self, dashboard):
|
def registered(self, dashboard):
|
||||||
|
@ -626,11 +624,11 @@ class Site(Registry, HorizonComponent):
|
||||||
return dash_instance._unregister(panel)
|
return dash_instance._unregister(panel)
|
||||||
|
|
||||||
def get_dashboard(self, dashboard):
|
def get_dashboard(self, dashboard):
|
||||||
""" Returns the specified :class:`~horizon.Dashboard` instance. """
|
"""Returns the specified :class:`~horizon.Dashboard` instance."""
|
||||||
return self._registered(dashboard)
|
return self._registered(dashboard)
|
||||||
|
|
||||||
def get_dashboards(self):
|
def get_dashboards(self):
|
||||||
""" 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
|
||||||
``HORIZON_CONFIG`` or else returns all registered dashboards
|
``HORIZON_CONFIG`` or else returns all registered dashboards
|
||||||
|
@ -658,7 +656,7 @@ class Site(Registry, HorizonComponent):
|
||||||
return dashboards
|
return dashboards
|
||||||
|
|
||||||
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 ``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
|
||||||
|
@ -672,7 +670,7 @@ class Site(Registry, HorizonComponent):
|
||||||
raise NotRegistered("No dashboard modules have been registered.")
|
raise NotRegistered("No dashboard modules have been registered.")
|
||||||
|
|
||||||
def get_user_home(self, user):
|
def get_user_home(self, user):
|
||||||
""" Returns the default URL for a particular user.
|
"""Returns the default URL for a particular user.
|
||||||
|
|
||||||
This method can be used to customize where a user is sent when
|
This method can be used to customize where a user is sent when
|
||||||
they log in, etc. By default it returns the value of
|
they log in, etc. By default it returns the value of
|
||||||
|
@ -710,7 +708,7 @@ class Site(Registry, HorizonComponent):
|
||||||
return self.get_absolute_url()
|
return self.get_absolute_url()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
""" Returns the default URL for Horizon's URLconf.
|
"""Returns the default URL for Horizon's URLconf.
|
||||||
|
|
||||||
The default URL is determined by calling
|
The default URL is determined by calling
|
||||||
:meth:`~horizon.Dashboard.get_absolute_url`
|
:meth:`~horizon.Dashboard.get_absolute_url`
|
||||||
|
@ -721,7 +719,7 @@ class Site(Registry, HorizonComponent):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _lazy_urls(self):
|
def _lazy_urls(self):
|
||||||
""" Lazy loading for URL patterns.
|
"""Lazy loading for URL patterns.
|
||||||
|
|
||||||
This method avoids problems associated with attempting to evaluate
|
This method avoids problems associated with attempting to evaluate
|
||||||
the the URLconf before the settings module has been loaded.
|
the the URLconf before the settings module has been loaded.
|
||||||
|
@ -732,7 +730,7 @@ class Site(Registry, HorizonComponent):
|
||||||
return LazyURLPattern(url_patterns), self.namespace, self.slug
|
return LazyURLPattern(url_patterns), self.namespace, self.slug
|
||||||
|
|
||||||
def _urls(self):
|
def _urls(self):
|
||||||
""" Constructs the URLconf for Horizon from registered Dashboards. """
|
"""Constructs the URLconf for Horizon from registered Dashboards."""
|
||||||
urlpatterns = self._get_default_urlpatterns()
|
urlpatterns = self._get_default_urlpatterns()
|
||||||
self._autodiscover()
|
self._autodiscover()
|
||||||
|
|
||||||
|
@ -764,7 +762,7 @@ class Site(Registry, HorizonComponent):
|
||||||
return urlpatterns, self.namespace, self.slug
|
return urlpatterns, self.namespace, self.slug
|
||||||
|
|
||||||
def _autodiscover(self):
|
def _autodiscover(self):
|
||||||
""" Discovers modules to register from ``settings.INSTALLED_APPS``.
|
"""Discovers modules to register from ``settings.INSTALLED_APPS``.
|
||||||
|
|
||||||
This makes sure that the appropriate modules get imported to register
|
This makes sure that the appropriate modules get imported to register
|
||||||
themselves with Horizon.
|
themselves with Horizon.
|
||||||
|
@ -787,8 +785,7 @@ class Site(Registry, HorizonComponent):
|
||||||
|
|
||||||
|
|
||||||
class HorizonSite(Site):
|
class HorizonSite(Site):
|
||||||
"""
|
"""A singleton implementation of Site such that all dealings with horizon
|
||||||
A singleton implementation of Site such that all dealings with horizon
|
|
||||||
get the same instance no matter what. There can be only one.
|
get the same instance no matter what. There can be only one.
|
||||||
"""
|
"""
|
||||||
_instance = None
|
_instance = None
|
||||||
|
|
|
@ -121,9 +121,8 @@ class ResourceBrowser(html.HTMLElement):
|
||||||
% (attr_name, self.__class__.__name__))
|
% (attr_name, self.__class__.__name__))
|
||||||
|
|
||||||
def set_tables(self, tables):
|
def set_tables(self, tables):
|
||||||
"""
|
"""Sets the table instances on the browser from a dictionary mapping
|
||||||
Sets the table instances on the browser from a dictionary mapping table
|
table names to table instances (as constructed by MultiTableView).
|
||||||
names to table instances (as constructed by MultiTableView).
|
|
||||||
"""
|
"""
|
||||||
self.navigation_table = tables[self.navigation_table_class._meta.name]
|
self.navigation_table = tables[self.navigation_table_class._meta.name]
|
||||||
self.content_table = tables[self.content_table_class._meta.name]
|
self.content_table = tables[self.content_table_class._meta.name]
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Breadcrumb(html.HTMLElement):
|
||||||
return self._subfolders
|
return self._subfolders
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" Renders the table using the template from the table options. """
|
"""Renders the table using the template from the table options."""
|
||||||
breadcrumb_template = template.loader.get_template(self.template)
|
breadcrumb_template = template.loader.get_template(self.template)
|
||||||
extra_context = {"breadcrumb": self}
|
extra_context = {"breadcrumb": self}
|
||||||
context = template.RequestContext(self.request, extra_context)
|
context = template.RequestContext(self.request, extra_context)
|
||||||
|
|
|
@ -25,7 +25,7 @@ from horizon import conf
|
||||||
|
|
||||||
|
|
||||||
def horizon(request):
|
def horizon(request):
|
||||||
""" The main Horizon context processor. Required for Horizon to function.
|
"""The main Horizon context processor. Required for Horizon to function.
|
||||||
|
|
||||||
It adds the Horizon config to the context as well as setting the names
|
It adds the Horizon config to the context as well as setting the names
|
||||||
``True`` and ``False`` in the context to their boolean equivalents
|
``True`` and ``False`` in the context to their boolean equivalents
|
||||||
|
|
|
@ -28,7 +28,7 @@ from django.utils.translation import ugettext_lazy as _ # noqa
|
||||||
|
|
||||||
|
|
||||||
def _current_component(view_func, dashboard=None, panel=None):
|
def _current_component(view_func, dashboard=None, panel=None):
|
||||||
""" Sets the currently-active dashboard and/or panel on the request. """
|
"""Sets the currently-active dashboard and/or panel on the request."""
|
||||||
@functools.wraps(view_func, assigned=available_attrs(view_func))
|
@functools.wraps(view_func, assigned=available_attrs(view_func))
|
||||||
def dec(request, *args, **kwargs):
|
def dec(request, *args, **kwargs):
|
||||||
if dashboard:
|
if dashboard:
|
||||||
|
@ -40,7 +40,7 @@ def _current_component(view_func, dashboard=None, panel=None):
|
||||||
|
|
||||||
|
|
||||||
def require_auth(view_func):
|
def require_auth(view_func):
|
||||||
""" Performs user authentication check.
|
"""Performs user authentication check.
|
||||||
|
|
||||||
Similar to Django's `login_required` decorator, except that this throws
|
Similar to Django's `login_required` decorator, except that this throws
|
||||||
:exc:`~horizon.exceptions.NotAuthenticated` exception if the user is not
|
:exc:`~horizon.exceptions.NotAuthenticated` exception if the user is not
|
||||||
|
@ -57,7 +57,7 @@ def require_auth(view_func):
|
||||||
|
|
||||||
|
|
||||||
def require_perms(view_func, required):
|
def require_perms(view_func, required):
|
||||||
""" Enforces permission-based access controls.
|
"""Enforces permission-based access controls.
|
||||||
|
|
||||||
:param list required: A tuple of permission names, all of which the request
|
:param list required: A tuple of permission names, all of which the request
|
||||||
user must possess in order access the decorated view.
|
user must possess in order access the decorated view.
|
||||||
|
|
|
@ -36,15 +36,14 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HorizonReporterFilter(SafeExceptionReporterFilter):
|
class HorizonReporterFilter(SafeExceptionReporterFilter):
|
||||||
""" Error report filter that's always active, even in DEBUG mode. """
|
"""Error report filter that's always active, even in DEBUG mode."""
|
||||||
def is_active(self, request):
|
def is_active(self, request):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# TODO(gabriel): This bugfix is cribbed from Django's code. When 1.4.1
|
# TODO(gabriel): This bugfix is cribbed from Django's code. When 1.4.1
|
||||||
# is available we can remove this code.
|
# is available we can remove this code.
|
||||||
def get_traceback_frame_variables(self, request, tb_frame):
|
def get_traceback_frame_variables(self, request, tb_frame):
|
||||||
"""
|
"""Replaces the values of variables marked as sensitive with
|
||||||
Replaces the values of variables marked as sensitive with
|
|
||||||
stars (*********).
|
stars (*********).
|
||||||
"""
|
"""
|
||||||
# Loop through the frame's callers to see if the sensitive_variables
|
# Loop through the frame's callers to see if the sensitive_variables
|
||||||
|
@ -93,13 +92,12 @@ class HorizonReporterFilter(SafeExceptionReporterFilter):
|
||||||
|
|
||||||
|
|
||||||
class HorizonException(Exception):
|
class HorizonException(Exception):
|
||||||
""" Base exception class for distinguishing our own exception classes. """
|
"""Base exception class for distinguishing our own exception classes."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Http302(HorizonException):
|
class Http302(HorizonException):
|
||||||
"""
|
"""Error class which can be raised from within a handler to cause an
|
||||||
Error class which can be raised from within a handler to cause an
|
|
||||||
early bailout and redirect at the middleware level.
|
early bailout and redirect at the middleware level.
|
||||||
"""
|
"""
|
||||||
status_code = 302
|
status_code = 302
|
||||||
|
@ -110,8 +108,7 @@ class Http302(HorizonException):
|
||||||
|
|
||||||
|
|
||||||
class NotAuthorized(HorizonException):
|
class NotAuthorized(HorizonException):
|
||||||
"""
|
"""Raised whenever a user attempts to access a resource which they do not
|
||||||
Raised whenever a user attempts to access a resource which they do not
|
|
||||||
have permission-based access to (such as when failing the
|
have permission-based access to (such as when failing the
|
||||||
:func:`~horizon.decorators.require_perms` decorator).
|
:func:`~horizon.decorators.require_perms` decorator).
|
||||||
|
|
||||||
|
@ -123,8 +120,8 @@ class NotAuthorized(HorizonException):
|
||||||
|
|
||||||
|
|
||||||
class NotAuthenticated(HorizonException):
|
class NotAuthenticated(HorizonException):
|
||||||
"""
|
"""Raised when a user is trying to make requests and they are not logged
|
||||||
Raised when a user is trying to make requests and they are not logged in.
|
in.
|
||||||
|
|
||||||
The included :class:`~horizon.middleware.HorizonMiddleware` catches
|
The included :class:`~horizon.middleware.HorizonMiddleware` catches
|
||||||
``NotAuthenticated`` and handles it gracefully by displaying an error
|
``NotAuthenticated`` and handles it gracefully by displaying an error
|
||||||
|
@ -134,19 +131,18 @@ class NotAuthenticated(HorizonException):
|
||||||
|
|
||||||
|
|
||||||
class NotFound(HorizonException):
|
class NotFound(HorizonException):
|
||||||
""" Generic error to replace all "Not Found"-type API errors. """
|
"""Generic error to replace all "Not Found"-type API errors."""
|
||||||
status_code = 404
|
status_code = 404
|
||||||
|
|
||||||
|
|
||||||
class RecoverableError(HorizonException):
|
class RecoverableError(HorizonException):
|
||||||
""" Generic error to replace any "Recoverable"-type API errors. """
|
"""Generic error to replace any "Recoverable"-type API errors."""
|
||||||
status_code = 100 # HTTP status code "Continue"
|
status_code = 100 # HTTP status code "Continue"
|
||||||
|
|
||||||
|
|
||||||
class ServiceCatalogException(HorizonException):
|
class ServiceCatalogException(HorizonException):
|
||||||
"""
|
"""Raised when a requested service is not available in the
|
||||||
Raised when a requested service is not available in the ``ServiceCatalog``
|
``ServiceCatalog`` returned by Keystone.
|
||||||
returned by Keystone.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, service_name):
|
def __init__(self, service_name):
|
||||||
message = 'Invalid service catalog service: %s' % service_name
|
message = 'Invalid service catalog service: %s' % service_name
|
||||||
|
@ -154,8 +150,7 @@ class ServiceCatalogException(HorizonException):
|
||||||
|
|
||||||
|
|
||||||
class AlreadyExists(HorizonException):
|
class AlreadyExists(HorizonException):
|
||||||
"""
|
"""Exception to be raised when trying to create an API resource which
|
||||||
Exception to be raised when trying to create an API resource which
|
|
||||||
already exists.
|
already exists.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, resource_type):
|
def __init__(self, name, resource_type):
|
||||||
|
@ -173,21 +168,19 @@ class AlreadyExists(HorizonException):
|
||||||
|
|
||||||
|
|
||||||
class WorkflowError(HorizonException):
|
class WorkflowError(HorizonException):
|
||||||
""" Exception to be raised when something goes wrong in a workflow. """
|
"""Exception to be raised when something goes wrong in a workflow."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WorkflowValidationError(HorizonException):
|
class WorkflowValidationError(HorizonException):
|
||||||
"""
|
"""Exception raised during workflow validation if required data is missing,
|
||||||
Exception raised during workflow validation if required data is missing,
|
|
||||||
or existing data is not valid.
|
or existing data is not valid.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class HandledException(HorizonException):
|
class HandledException(HorizonException):
|
||||||
"""
|
"""Used internally to track exceptions that have gone through
|
||||||
Used internally to track exceptions that have gone through
|
|
||||||
:func:`horizon.exceptions.handle` more than once.
|
:func:`horizon.exceptions.handle` more than once.
|
||||||
"""
|
"""
|
||||||
def __init__(self, wrapped):
|
def __init__(self, wrapped):
|
||||||
|
@ -205,8 +198,7 @@ def error_color(msg):
|
||||||
|
|
||||||
|
|
||||||
def check_message(keywords, message):
|
def check_message(keywords, message):
|
||||||
"""
|
"""Checks an exception for given keywords and raises a new ``ActionError``
|
||||||
Checks an exception for given keywords and raises a new ``ActionError``
|
|
||||||
with the desired message if the keywords are found. This allows selective
|
with the desired message if the keywords are found. This allows selective
|
||||||
control over API error messages.
|
control over API error messages.
|
||||||
"""
|
"""
|
||||||
|
@ -218,7 +210,7 @@ def check_message(keywords, message):
|
||||||
|
|
||||||
def handle(request, message=None, redirect=None, ignore=False,
|
def handle(request, message=None, redirect=None, ignore=False,
|
||||||
escalate=False, log_level=None, force_log=None):
|
escalate=False, log_level=None, force_log=None):
|
||||||
""" Centralized error handling for Horizon.
|
"""Centralized error handling for Horizon.
|
||||||
|
|
||||||
Because Horizon consumes so many different APIs with completely
|
Because Horizon consumes so many different APIs with completely
|
||||||
different ``Exception`` types, it's necessary to have a centralized
|
different ``Exception`` types, it's necessary to have a centralized
|
||||||
|
|
|
@ -32,15 +32,13 @@ class SelfHandlingMixin(object):
|
||||||
|
|
||||||
|
|
||||||
class SelfHandlingForm(SelfHandlingMixin, forms.Form):
|
class SelfHandlingForm(SelfHandlingMixin, forms.Form):
|
||||||
"""
|
"""A base :class:`Form <django:django.forms.Form>` class which includes
|
||||||
A base :class:`Form <django:django.forms.Form>` class which includes
|
|
||||||
processing logic in its subclasses.
|
processing logic in its subclasses.
|
||||||
"""
|
"""
|
||||||
required_css_class = 'required'
|
required_css_class = 'required'
|
||||||
|
|
||||||
def api_error(self, message):
|
def api_error(self, message):
|
||||||
"""
|
"""Adds an error to the form's error dictionary after validation
|
||||||
Adds an error to the form's error dictionary after validation
|
|
||||||
based on problems reported via the API. This is useful when you
|
based on problems reported via the API. This is useful when you
|
||||||
wish for API errors to appear as errors on the form rather than
|
wish for API errors to appear as errors on the form rather than
|
||||||
using the messages framework.
|
using the messages framework.
|
||||||
|
@ -49,7 +47,7 @@ class SelfHandlingForm(SelfHandlingMixin, forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class DateForm(forms.Form):
|
class DateForm(forms.Form):
|
||||||
""" A simple form for selecting a range of time. """
|
"""A simple form for selecting a range of time."""
|
||||||
start = forms.DateField(input_formats=("%Y-%m-%d",))
|
start = forms.DateField(input_formats=("%Y-%m-%d",))
|
||||||
end = forms.DateField(input_formats=("%Y-%m-%d",))
|
end = forms.DateField(input_formats=("%Y-%m-%d",))
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,8 @@ from django.forms import widgets
|
||||||
|
|
||||||
|
|
||||||
class DynamicSelectWidget(widgets.Select):
|
class DynamicSelectWidget(widgets.Select):
|
||||||
"""
|
"""A subclass of the ``Select`` widget which renders extra attributes for
|
||||||
A subclass of the ``Select`` widget which renders extra attributes for use
|
use in callbacks to handle dynamic changes to the available choices.
|
||||||
in callbacks to handle dynamic changes to the available choices.
|
|
||||||
"""
|
"""
|
||||||
_data_add_url_attr = "data-add-item-url"
|
_data_add_url_attr = "data-add-item-url"
|
||||||
|
|
||||||
|
@ -46,8 +45,7 @@ class DynamicSelectWidget(widgets.Select):
|
||||||
|
|
||||||
|
|
||||||
class DynamicChoiceField(fields.ChoiceField):
|
class DynamicChoiceField(fields.ChoiceField):
|
||||||
"""
|
"""A subclass of ``ChoiceField`` with additional properties that make
|
||||||
A subclass of ``ChoiceField`` with additional properties that make
|
|
||||||
dynamically updating its elements easier.
|
dynamically updating its elements easier.
|
||||||
|
|
||||||
Notably, the field declaration takes an extra argument, ``add_item_link``
|
Notably, the field declaration takes an extra argument, ``add_item_link``
|
||||||
|
@ -67,5 +65,5 @@ class DynamicChoiceField(fields.ChoiceField):
|
||||||
|
|
||||||
|
|
||||||
class DynamicTypedChoiceField(DynamicChoiceField, fields.TypedChoiceField):
|
class DynamicTypedChoiceField(DynamicChoiceField, fields.TypedChoiceField):
|
||||||
""" Simple mix of ``DynamicChoiceField`` and ``TypedChoiceField``. """
|
"""Simple mix of ``DynamicChoiceField`` and ``TypedChoiceField``."""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -49,8 +49,7 @@ class ModalFormMixin(object):
|
||||||
|
|
||||||
|
|
||||||
class ModalFormView(ModalFormMixin, generic.FormView):
|
class ModalFormView(ModalFormMixin, generic.FormView):
|
||||||
"""
|
"""The main view class from which all views which handle forms in Horizon
|
||||||
The main view class from which all views which handle forms in Horizon
|
|
||||||
should inherit. It takes care of all details with processing
|
should inherit. It takes care of all details with processing
|
||||||
:class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns
|
:class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns
|
||||||
when the associated template inherits from
|
when the associated template inherits from
|
||||||
|
@ -65,25 +64,21 @@ class ModalFormView(ModalFormMixin, generic.FormView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_object_id(self, obj):
|
def get_object_id(self, obj):
|
||||||
"""
|
"""For dynamic insertion of resources created in modals, this method
|
||||||
For dynamic insertion of resources created in modals, this method
|
|
||||||
returns the id of the created object. Defaults to returning the ``id``
|
returns the id of the created object. Defaults to returning the ``id``
|
||||||
attribute.
|
attribute.
|
||||||
"""
|
"""
|
||||||
return obj.id
|
return obj.id
|
||||||
|
|
||||||
def get_object_display(self, obj):
|
def get_object_display(self, obj):
|
||||||
"""
|
"""For dynamic insertion of resources created in modals, this method
|
||||||
For dynamic insertion of resources created in modals, this method
|
|
||||||
returns the display name of the created object. Defaults to returning
|
returns the display name of the created object. Defaults to returning
|
||||||
the ``name`` attribute.
|
the ``name`` attribute.
|
||||||
"""
|
"""
|
||||||
return obj.name
|
return obj.name
|
||||||
|
|
||||||
def get_form(self, form_class):
|
def get_form(self, form_class):
|
||||||
"""
|
"""Returns an instance of the form to be used in this view."""
|
||||||
Returns an instance of the form to be used in this view.
|
|
||||||
"""
|
|
||||||
return form_class(self.request, **self.get_form_kwargs())
|
return form_class(self.request, **self.get_form_kwargs())
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
|
|
@ -26,9 +26,7 @@ from django.utils.safestring import SafeData # noqa
|
||||||
|
|
||||||
|
|
||||||
def add_message(request, level, message, extra_tags='', fail_silently=False):
|
def add_message(request, level, message, extra_tags='', fail_silently=False):
|
||||||
"""
|
"""Attempts to add a message to the request using the 'messages' app."""
|
||||||
Attempts to add a message to the request using the 'messages' app.
|
|
||||||
"""
|
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
tag = constants.DEFAULT_TAGS[level]
|
tag = constants.DEFAULT_TAGS[level]
|
||||||
# if message is marked as safe, pass "safe" tag as extra_tags so that
|
# if message is marked as safe, pass "safe" tag as extra_tags so that
|
||||||
|
@ -44,40 +42,30 @@ def add_message(request, level, message, extra_tags='', fail_silently=False):
|
||||||
|
|
||||||
|
|
||||||
def debug(request, message, extra_tags='', fail_silently=False):
|
def debug(request, message, extra_tags='', fail_silently=False):
|
||||||
"""
|
"""Adds a message with the ``DEBUG`` level."""
|
||||||
Adds a message with the ``DEBUG`` level.
|
|
||||||
"""
|
|
||||||
add_message(request, constants.DEBUG, message, extra_tags=extra_tags,
|
add_message(request, constants.DEBUG, message, extra_tags=extra_tags,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
|
|
||||||
|
|
||||||
def info(request, message, extra_tags='', fail_silently=False):
|
def info(request, message, extra_tags='', fail_silently=False):
|
||||||
"""
|
"""Adds a message with the ``INFO`` level."""
|
||||||
Adds a message with the ``INFO`` level.
|
|
||||||
"""
|
|
||||||
add_message(request, constants.INFO, message, extra_tags=extra_tags,
|
add_message(request, constants.INFO, message, extra_tags=extra_tags,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
|
|
||||||
|
|
||||||
def success(request, message, extra_tags='', fail_silently=False):
|
def success(request, message, extra_tags='', fail_silently=False):
|
||||||
"""
|
"""Adds a message with the ``SUCCESS`` level."""
|
||||||
Adds a message with the ``SUCCESS`` level.
|
|
||||||
"""
|
|
||||||
add_message(request, constants.SUCCESS, message, extra_tags=extra_tags,
|
add_message(request, constants.SUCCESS, message, extra_tags=extra_tags,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
|
|
||||||
|
|
||||||
def warning(request, message, extra_tags='', fail_silently=False):
|
def warning(request, message, extra_tags='', fail_silently=False):
|
||||||
"""
|
"""Adds a message with the ``WARNING`` level."""
|
||||||
Adds a message with the ``WARNING`` level.
|
|
||||||
"""
|
|
||||||
add_message(request, constants.WARNING, message, extra_tags=extra_tags,
|
add_message(request, constants.WARNING, message, extra_tags=extra_tags,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
|
|
||||||
|
|
||||||
def error(request, message, extra_tags='', fail_silently=False):
|
def error(request, message, extra_tags='', fail_silently=False):
|
||||||
"""
|
"""Adds a message with the ``ERROR`` level."""
|
||||||
Adds a message with the ``ERROR`` level.
|
|
||||||
"""
|
|
||||||
add_message(request, constants.ERROR, message, extra_tags=extra_tags,
|
add_message(request, constants.ERROR, message, extra_tags=extra_tags,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
|
|
|
@ -43,12 +43,12 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HorizonMiddleware(object):
|
class HorizonMiddleware(object):
|
||||||
""" The main Horizon middleware class. Required for use of Horizon. """
|
"""The main Horizon middleware class. Required for use of Horizon."""
|
||||||
|
|
||||||
logout_reason = None
|
logout_reason = None
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
""" Adds data necessary for Horizon to function to the request. """
|
"""Adds data necessary for Horizon to function to the request."""
|
||||||
# Activate timezone handling
|
# Activate timezone handling
|
||||||
tz = request.session.get('django_timezone')
|
tz = request.session.get('django_timezone')
|
||||||
if tz:
|
if tz:
|
||||||
|
@ -75,8 +75,7 @@ class HorizonMiddleware(object):
|
||||||
request.session['last_activity'] = timestamp
|
request.session['last_activity'] = timestamp
|
||||||
|
|
||||||
def process_exception(self, request, exception):
|
def process_exception(self, request, exception):
|
||||||
"""
|
"""Catches internal Horizon exception classes such as NotAuthorized,
|
||||||
Catches internal Horizon exception classes such as NotAuthorized,
|
|
||||||
NotFound and Http302 and handles them gracefully.
|
NotFound and Http302 and handles them gracefully.
|
||||||
"""
|
"""
|
||||||
if isinstance(exception, (exceptions.NotAuthorized,
|
if isinstance(exception, (exceptions.NotAuthorized,
|
||||||
|
@ -108,8 +107,7 @@ class HorizonMiddleware(object):
|
||||||
return shortcuts.redirect(exception.location)
|
return shortcuts.redirect(exception.location)
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
"""
|
"""Convert HttpResponseRedirect to HttpResponse if request is via ajax
|
||||||
Convert HttpResponseRedirect to HttpResponse if request is via ajax
|
|
||||||
to allow ajax request to redirect url
|
to allow ajax request to redirect url
|
||||||
"""
|
"""
|
||||||
if request.is_ajax() and hasattr(request, 'horizon'):
|
if request.is_ajax() and hasattr(request, 'horizon'):
|
||||||
|
|
|
@ -39,7 +39,7 @@ STRING_SEPARATOR = "__"
|
||||||
|
|
||||||
|
|
||||||
class BaseAction(html.HTMLElement):
|
class BaseAction(html.HTMLElement):
|
||||||
""" Common base class for all ``Action`` classes. """
|
"""Common base class for all ``Action`` classes."""
|
||||||
table = None
|
table = None
|
||||||
handles_multiple = False
|
handles_multiple = False
|
||||||
requires_input = False
|
requires_input = False
|
||||||
|
@ -51,8 +51,8 @@ class BaseAction(html.HTMLElement):
|
||||||
self.datum = datum
|
self.datum = datum
|
||||||
|
|
||||||
def data_type_matched(self, datum):
|
def data_type_matched(self, datum):
|
||||||
""" Method to see if the action is allowed for a certain type of data.
|
"""Method to see if the action is allowed for a certain type of data.
|
||||||
Only affects mixed data type tables.
|
Only affects mixed data type tables.
|
||||||
"""
|
"""
|
||||||
if datum:
|
if datum:
|
||||||
action_data_types = getattr(self, "allowed_data_types", [])
|
action_data_types = getattr(self, "allowed_data_types", [])
|
||||||
|
@ -66,7 +66,7 @@ class BaseAction(html.HTMLElement):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_policy_target(self, request, datum):
|
def get_policy_target(self, request, datum):
|
||||||
""" Provide the target for a policy request.
|
"""Provide the target for a policy request.
|
||||||
|
|
||||||
This method is meant to be overridden to return target details when
|
This method is meant to be overridden to return target details when
|
||||||
one of the policy checks requires them. E.g., {"user_id": datum.id}
|
one of the policy checks requires them. E.g., {"user_id": datum.id}
|
||||||
|
@ -74,7 +74,7 @@ class BaseAction(html.HTMLElement):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def allowed(self, request, datum):
|
def allowed(self, request, datum):
|
||||||
""" Determine whether this action is allowed for the current request.
|
"""Determine whether this action is allowed for the current request.
|
||||||
|
|
||||||
This method is meant to be overridden with more specific checks.
|
This method is meant to be overridden with more specific checks.
|
||||||
"""
|
"""
|
||||||
|
@ -90,7 +90,7 @@ class BaseAction(html.HTMLElement):
|
||||||
return self.allowed(request, datum)
|
return self.allowed(request, datum)
|
||||||
|
|
||||||
def update(self, request, datum):
|
def update(self, request, datum):
|
||||||
""" Allows per-action customization based on current conditions.
|
"""Allows per-action customization based on current conditions.
|
||||||
|
|
||||||
This is particularly useful when you wish to create a "toggle"
|
This is particularly useful when you wish to create a "toggle"
|
||||||
action that will be rendered differently based on the value of an
|
action that will be rendered differently based on the value of an
|
||||||
|
@ -101,16 +101,14 @@ class BaseAction(html.HTMLElement):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_default_classes(self):
|
def get_default_classes(self):
|
||||||
"""
|
"""Returns a list of the default classes for the action. Defaults to
|
||||||
Returns a list of the default classes for the action. Defaults to
|
|
||||||
``["btn", "btn-small"]``.
|
``["btn", "btn-small"]``.
|
||||||
"""
|
"""
|
||||||
return getattr(settings, "ACTION_CSS_CLASSES", ACTION_CSS_CLASSES)
|
return getattr(settings, "ACTION_CSS_CLASSES", ACTION_CSS_CLASSES)
|
||||||
|
|
||||||
def get_default_attrs(self):
|
def get_default_attrs(self):
|
||||||
"""
|
"""Returns a list of the default HTML attributes for the action.
|
||||||
Returns a list of the default HTML attributes for the action. Defaults
|
Defaults to returning an ``id`` attribute with the value
|
||||||
to returning an ``id`` attribute with the value
|
|
||||||
``{{ table.name }}__action_{{ action.name }}__{{ creation counter }}``.
|
``{{ table.name }}__action_{{ action.name }}__{{ creation counter }}``.
|
||||||
"""
|
"""
|
||||||
if self.datum is not None:
|
if self.datum is not None:
|
||||||
|
@ -126,7 +124,7 @@ class BaseAction(html.HTMLElement):
|
||||||
|
|
||||||
|
|
||||||
class Action(BaseAction):
|
class Action(BaseAction):
|
||||||
""" Represents an action which can be taken on this table's data.
|
"""Represents an action which can be taken on this table's data.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
|
@ -265,7 +263,7 @@ class Action(BaseAction):
|
||||||
self.multiple = new.instancemethod(multiple, self)
|
self.multiple = new.instancemethod(multiple, self)
|
||||||
|
|
||||||
def get_param_name(self):
|
def get_param_name(self):
|
||||||
""" Returns the full POST parameter name for this action.
|
"""Returns the full POST parameter name for this action.
|
||||||
|
|
||||||
Defaults to
|
Defaults to
|
||||||
``{{ table.name }}__{{ action.name }}``.
|
``{{ table.name }}__{{ action.name }}``.
|
||||||
|
@ -274,7 +272,7 @@ class Action(BaseAction):
|
||||||
|
|
||||||
|
|
||||||
class LinkAction(BaseAction):
|
class LinkAction(BaseAction):
|
||||||
""" A table action which is simply a link rather than a form POST.
|
"""A table action which is simply a link rather than a form POST.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
|
@ -319,7 +317,7 @@ class LinkAction(BaseAction):
|
||||||
self.attrs.update(attrs)
|
self.attrs.update(attrs)
|
||||||
|
|
||||||
def get_link_url(self, datum=None):
|
def get_link_url(self, datum=None):
|
||||||
""" Returns the final URL based on the value of ``url``.
|
"""Returns the final URL based on the value of ``url``.
|
||||||
|
|
||||||
If ``url`` is callable it will call the function.
|
If ``url`` is callable it will call the function.
|
||||||
If not, it will then try to call ``reverse`` on ``url``.
|
If not, it will then try to call ``reverse`` on ``url``.
|
||||||
|
@ -346,7 +344,7 @@ class LinkAction(BaseAction):
|
||||||
|
|
||||||
|
|
||||||
class FilterAction(BaseAction):
|
class FilterAction(BaseAction):
|
||||||
""" A base class representing a filter action for a table.
|
"""A base class representing a filter action for a table.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
|
@ -389,7 +387,7 @@ class FilterAction(BaseAction):
|
||||||
self.param_name = param_name or 'q'
|
self.param_name = param_name or 'q'
|
||||||
|
|
||||||
def get_param_name(self):
|
def get_param_name(self):
|
||||||
""" Returns the full query parameter name for this action.
|
"""Returns the full query parameter name for this action.
|
||||||
|
|
||||||
Defaults to
|
Defaults to
|
||||||
``{{ table.name }}__{{ action.name }}__{{ action.param_name }}``.
|
``{{ table.name }}__{{ action.name }}__{{ action.param_name }}``.
|
||||||
|
@ -424,7 +422,7 @@ class FilterAction(BaseAction):
|
||||||
return filtered_data
|
return filtered_data
|
||||||
|
|
||||||
def filter(self, table, data, filter_string):
|
def filter(self, table, data, filter_string):
|
||||||
""" Provides the actual filtering logic.
|
"""Provides the actual filtering logic.
|
||||||
|
|
||||||
This method must be overridden by subclasses and return
|
This method must be overridden by subclasses and return
|
||||||
the filtered data.
|
the filtered data.
|
||||||
|
@ -434,8 +432,7 @@ class FilterAction(BaseAction):
|
||||||
|
|
||||||
|
|
||||||
class FixedFilterAction(FilterAction):
|
class FixedFilterAction(FilterAction):
|
||||||
""" A filter action with fixed buttons.
|
"""A filter action with fixed buttons."""
|
||||||
"""
|
|
||||||
filter_type = 'fixed'
|
filter_type = 'fixed'
|
||||||
needs_preloading = True
|
needs_preloading = True
|
||||||
|
|
||||||
|
@ -480,9 +477,9 @@ class FixedFilterAction(FilterAction):
|
||||||
|
|
||||||
|
|
||||||
class BatchAction(Action):
|
class BatchAction(Action):
|
||||||
""" A table action which takes batch action on one or more
|
"""A table action which takes batch action on one or more
|
||||||
objects. This action should not require user input on a
|
objects. This action should not require user input on a
|
||||||
per-object basis.
|
per-object basis.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
|
@ -543,8 +540,7 @@ class BatchAction(Action):
|
||||||
return super(BatchAction, self)._allowed(request, datum)
|
return super(BatchAction, self)._allowed(request, datum)
|
||||||
|
|
||||||
def _conjugate(self, items=None, past=False):
|
def _conjugate(self, items=None, past=False):
|
||||||
"""
|
"""Builds combinations like 'Delete Object' and 'Deleted
|
||||||
Builds combinations like 'Delete Object' and 'Deleted
|
|
||||||
Objects' based on the number of items and `past` flag.
|
Objects' based on the number of items and `past` flag.
|
||||||
"""
|
"""
|
||||||
action_type = "past" if past else "present"
|
action_type = "past" if past else "present"
|
||||||
|
@ -565,8 +561,8 @@ class BatchAction(Action):
|
||||||
return msgstr % {'action': action, 'data_type': data_type}
|
return msgstr % {'action': action, 'data_type': data_type}
|
||||||
|
|
||||||
def action(self, request, datum_id):
|
def action(self, request, datum_id):
|
||||||
"""
|
"""Required. Accepts a single object id and performs the specific
|
||||||
Required. Accepts a single object id and performs the specific action.
|
action.
|
||||||
|
|
||||||
Return values are discarded, errors raised are caught and logged.
|
Return values are discarded, errors raised are caught and logged.
|
||||||
"""
|
"""
|
||||||
|
@ -574,17 +570,13 @@ class BatchAction(Action):
|
||||||
'BatchAction: %s' % self.data_type_singular)
|
'BatchAction: %s' % self.data_type_singular)
|
||||||
|
|
||||||
def update(self, request, datum):
|
def update(self, request, datum):
|
||||||
"""
|
"""Switches the action verbose name, if needed."""
|
||||||
Switches the action verbose name, if needed
|
|
||||||
"""
|
|
||||||
if getattr(self, 'action_present', False):
|
if getattr(self, 'action_present', False):
|
||||||
self.verbose_name = self._conjugate()
|
self.verbose_name = self._conjugate()
|
||||||
self.verbose_name_plural = self._conjugate('plural')
|
self.verbose_name_plural = self._conjugate('plural')
|
||||||
|
|
||||||
def get_success_url(self, request=None):
|
def get_success_url(self, request=None):
|
||||||
"""
|
"""Returns the URL to redirect to after a successful action."""
|
||||||
Returns the URL to redirect to after a successful action.
|
|
||||||
"""
|
|
||||||
if self.success_url:
|
if self.success_url:
|
||||||
return self.success_url
|
return self.success_url
|
||||||
return request.get_full_path()
|
return request.get_full_path()
|
||||||
|
|
|
@ -49,7 +49,7 @@ STRING_SEPARATOR = "__"
|
||||||
|
|
||||||
|
|
||||||
class Column(html.HTMLElement):
|
class Column(html.HTMLElement):
|
||||||
""" A class which represents a single column in a :class:`.DataTable`.
|
"""A class which represents a single column in a :class:`.DataTable`.
|
||||||
|
|
||||||
.. attribute:: transform
|
.. attribute:: transform
|
||||||
|
|
||||||
|
@ -270,10 +270,9 @@ class Column(html.HTMLElement):
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.name)
|
return '<%s: %s>' % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
def get_raw_data(self, datum):
|
def get_raw_data(self, datum):
|
||||||
"""
|
"""Returns the raw data for this column, before any filters or
|
||||||
Returns the raw data for this column, before any filters or formatting
|
formatting are applied to it. This is useful when doing calculations
|
||||||
are applied to it. This is useful when doing calculations on data in
|
on data in the table.
|
||||||
the table.
|
|
||||||
"""
|
"""
|
||||||
# Callable transformations
|
# Callable transformations
|
||||||
if callable(self.transform):
|
if callable(self.transform):
|
||||||
|
@ -295,8 +294,8 @@ class Column(html.HTMLElement):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_data(self, datum):
|
def get_data(self, datum):
|
||||||
"""
|
"""Returns the final display data for this column from the given
|
||||||
Returns the final display data for this column from the given inputs.
|
inputs.
|
||||||
|
|
||||||
The return value will be either the attribute specified for this column
|
The return value will be either the attribute specified for this column
|
||||||
or the return value of the attr:`~horizon.tables.Column.transform`
|
or the return value of the attr:`~horizon.tables.Column.transform`
|
||||||
|
@ -329,7 +328,7 @@ class Column(html.HTMLElement):
|
||||||
return self.table._data_cache[self][datum_id]
|
return self.table._data_cache[self][datum_id]
|
||||||
|
|
||||||
def get_link_url(self, datum):
|
def get_link_url(self, datum):
|
||||||
""" Returns the final value for the column's ``link`` property.
|
"""Returns the final value for the column's ``link`` property.
|
||||||
|
|
||||||
If ``allowed_data_types`` of this column is not empty and the datum
|
If ``allowed_data_types`` of this column is not empty and the datum
|
||||||
has an assigned type, check if the datum's type is in the
|
has an assigned type, check if the datum's type is in the
|
||||||
|
@ -355,8 +354,7 @@ class Column(html.HTMLElement):
|
||||||
return self.link
|
return self.link
|
||||||
|
|
||||||
def get_summation(self):
|
def get_summation(self):
|
||||||
"""
|
"""Returns the summary value for the data in this column if a
|
||||||
Returns the summary value for the data in this column if a
|
|
||||||
valid summation method is specified for it. Otherwise returns ``None``.
|
valid summation method is specified for it. Otherwise returns ``None``.
|
||||||
"""
|
"""
|
||||||
if self.summation not in self.summation_methods:
|
if self.summation not in self.summation_methods:
|
||||||
|
@ -376,7 +374,7 @@ class Column(html.HTMLElement):
|
||||||
|
|
||||||
|
|
||||||
class Row(html.HTMLElement):
|
class Row(html.HTMLElement):
|
||||||
""" Represents a row in the table.
|
"""Represents a row in the table.
|
||||||
|
|
||||||
When iterated, the ``Row`` instance will yield each of its cells.
|
When iterated, the ``Row`` instance will yield each of its cells.
|
||||||
|
|
||||||
|
@ -444,8 +442,7 @@ class Row(html.HTMLElement):
|
||||||
self.cells = []
|
self.cells = []
|
||||||
|
|
||||||
def load_cells(self, datum=None):
|
def load_cells(self, datum=None):
|
||||||
"""
|
"""Load the row's data (either provided at initialization or as an
|
||||||
Load the row's data (either provided at initialization or as an
|
|
||||||
argument to this function), initiailize all the cells contained
|
argument to this function), initiailize all the cells contained
|
||||||
by this row, and set the appropriate row properties which require
|
by this row, and set the appropriate row properties which require
|
||||||
the row's data to be determined.
|
the row's data to be determined.
|
||||||
|
@ -526,7 +523,7 @@ class Row(html.HTMLElement):
|
||||||
{"row": self})
|
{"row": self})
|
||||||
|
|
||||||
def get_cells(self):
|
def get_cells(self):
|
||||||
""" Returns the bound cells for this row in order. """
|
"""Returns the bound cells for this row in order."""
|
||||||
return self.cells.values()
|
return self.cells.values()
|
||||||
|
|
||||||
def get_ajax_update_url(self):
|
def get_ajax_update_url(self):
|
||||||
|
@ -537,8 +534,7 @@ class Row(html.HTMLElement):
|
||||||
return "%s?%s" % (table_url, params)
|
return "%s?%s" % (table_url, params)
|
||||||
|
|
||||||
def get_data(self, request, obj_id):
|
def get_data(self, request, obj_id):
|
||||||
"""
|
"""Fetches the updated data for the row based on the object id
|
||||||
Fetches the updated data for the row based on the object id
|
|
||||||
passed in. Must be implemented by a subclass to allow AJAX updating.
|
passed in. Must be implemented by a subclass to allow AJAX updating.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("You must define a get_data method on %s"
|
raise NotImplementedError("You must define a get_data method on %s"
|
||||||
|
@ -546,7 +542,7 @@ class Row(html.HTMLElement):
|
||||||
|
|
||||||
|
|
||||||
class Cell(html.HTMLElement):
|
class Cell(html.HTMLElement):
|
||||||
""" Represents a single cell in the table. """
|
"""Represents a single cell in the table."""
|
||||||
def __init__(self, datum, data, column, row, attrs=None, classes=None):
|
def __init__(self, datum, data, column, row, attrs=None, classes=None):
|
||||||
self.classes = classes or getattr(self, "classes", [])
|
self.classes = classes or getattr(self, "classes", [])
|
||||||
super(Cell, self).__init__()
|
super(Cell, self).__init__()
|
||||||
|
@ -565,8 +561,7 @@ class Cell(html.HTMLElement):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
"""
|
"""Returns a formatted version of the data for final output.
|
||||||
Returns a formatted version of the data for final output.
|
|
||||||
|
|
||||||
This takes into consideration the
|
This takes into consideration the
|
||||||
:attr:`~horizon.tables.Column.link`` and
|
:attr:`~horizon.tables.Column.link`` and
|
||||||
|
@ -602,7 +597,7 @@ class Cell(html.HTMLElement):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
""" Gets the status for the column based on the cell's data. """
|
"""Gets the status for the column based on the cell's data."""
|
||||||
# Deal with status column mechanics based in this cell's data
|
# Deal with status column mechanics based in this cell's data
|
||||||
if hasattr(self, '_status'):
|
if hasattr(self, '_status'):
|
||||||
return self._status
|
return self._status
|
||||||
|
@ -619,7 +614,7 @@ class Cell(html.HTMLElement):
|
||||||
return self._status
|
return self._status
|
||||||
|
|
||||||
def get_status_class(self, status):
|
def get_status_class(self, status):
|
||||||
""" Returns a css class name determined by the status value. """
|
"""Returns a css class name determined by the status value."""
|
||||||
if status is True:
|
if status is True:
|
||||||
return "status_up"
|
return "status_up"
|
||||||
elif status is False:
|
elif status is False:
|
||||||
|
@ -628,7 +623,7 @@ class Cell(html.HTMLElement):
|
||||||
return "status_unknown"
|
return "status_unknown"
|
||||||
|
|
||||||
def get_default_classes(self):
|
def get_default_classes(self):
|
||||||
""" Returns a flattened string of the cell's CSS classes. """
|
"""Returns a flattened string of the cell's CSS classes."""
|
||||||
if not self.url:
|
if not self.url:
|
||||||
self.column.classes = [cls for cls in self.column.classes
|
self.column.classes = [cls for cls in self.column.classes
|
||||||
if cls != "anchor"]
|
if cls != "anchor"]
|
||||||
|
@ -640,7 +635,7 @@ class Cell(html.HTMLElement):
|
||||||
|
|
||||||
|
|
||||||
class DataTableOptions(object):
|
class DataTableOptions(object):
|
||||||
""" Contains options for :class:`.DataTable` objects.
|
"""Contains options for :class:`.DataTable` objects.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
|
@ -823,7 +818,7 @@ class DataTableOptions(object):
|
||||||
|
|
||||||
|
|
||||||
class DataTableMetaclass(type):
|
class DataTableMetaclass(type):
|
||||||
""" Metaclass to add options to DataTable class and collect columns. """
|
"""Metaclass to add options to DataTable class and collect columns."""
|
||||||
def __new__(mcs, name, bases, attrs):
|
def __new__(mcs, name, bases, attrs):
|
||||||
# Process options from Meta
|
# Process options from Meta
|
||||||
class_name = name
|
class_name = name
|
||||||
|
@ -896,7 +891,7 @@ class DataTableMetaclass(type):
|
||||||
|
|
||||||
|
|
||||||
class DataTable(object):
|
class DataTable(object):
|
||||||
""" A class which defines a table with all data and associated actions.
|
"""A class which defines a table with all data and associated actions.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
|
||||||
|
@ -1017,14 +1012,14 @@ class DataTable(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" Renders the table using the template from the table options. """
|
"""Renders the table using the template from the table options."""
|
||||||
table_template = template.loader.get_template(self._meta.template)
|
table_template = template.loader.get_template(self._meta.template)
|
||||||
extra_context = {self._meta.context_var_name: self}
|
extra_context = {self._meta.context_var_name: self}
|
||||||
context = template.RequestContext(self.request, extra_context)
|
context = template.RequestContext(self.request, extra_context)
|
||||||
return table_template.render(context)
|
return table_template.render(context)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
""" Returns the canonical URL for this table.
|
"""Returns the canonical URL for this table.
|
||||||
|
|
||||||
This is used for the POST action attribute on the form element
|
This is used for the POST action attribute on the form element
|
||||||
wrapping the table. In many cases it is also useful for redirecting
|
wrapping the table. In many cases it is also useful for redirecting
|
||||||
|
@ -1037,12 +1032,11 @@ class DataTable(object):
|
||||||
return self.request.get_full_path().partition('?')[0]
|
return self.request.get_full_path().partition('?')[0]
|
||||||
|
|
||||||
def get_empty_message(self):
|
def get_empty_message(self):
|
||||||
""" Returns the message to be displayed when there is no data. """
|
"""Returns the message to be displayed when there is no data."""
|
||||||
return self._no_data_message
|
return self._no_data_message
|
||||||
|
|
||||||
def get_object_by_id(self, lookup):
|
def get_object_by_id(self, lookup):
|
||||||
"""
|
"""Returns the data object from the table's dataset which matches
|
||||||
Returns the data object from the table's dataset which matches
|
|
||||||
the ``lookup`` parameter specified. An error will be raised if
|
the ``lookup`` parameter specified. An error will be raised if
|
||||||
the match is not a single data object.
|
the match is not a single data object.
|
||||||
|
|
||||||
|
@ -1071,8 +1065,7 @@ class DataTable(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_actions(self):
|
def has_actions(self):
|
||||||
"""
|
"""Boolean. Indicates whether there are any available actions on this
|
||||||
Boolean. Indicates whether there are any available actions on this
|
|
||||||
table.
|
table.
|
||||||
"""
|
"""
|
||||||
if not self.base_actions:
|
if not self.base_actions:
|
||||||
|
@ -1081,8 +1074,7 @@ class DataTable(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def needs_form_wrapper(self):
|
def needs_form_wrapper(self):
|
||||||
"""
|
"""Boolean. Indicates whather this table should be rendered wrapped in
|
||||||
Boolean. Indicates whather this table should be rendered wrapped in
|
|
||||||
a ``<form>`` tag or not.
|
a ``<form>`` tag or not.
|
||||||
"""
|
"""
|
||||||
# If needs_form_wrapper is explicitly set, defer to that.
|
# If needs_form_wrapper is explicitly set, defer to that.
|
||||||
|
@ -1092,14 +1084,14 @@ class DataTable(object):
|
||||||
return self.has_actions
|
return self.has_actions
|
||||||
|
|
||||||
def get_table_actions(self):
|
def get_table_actions(self):
|
||||||
""" Returns a list of the action instances for this table. """
|
"""Returns a list of the action instances for this table."""
|
||||||
bound_actions = [self.base_actions[action.name] for
|
bound_actions = [self.base_actions[action.name] for
|
||||||
action in self._meta.table_actions]
|
action in self._meta.table_actions]
|
||||||
return [action for action in bound_actions if
|
return [action for action in bound_actions if
|
||||||
self._filter_action(action, self.request)]
|
self._filter_action(action, self.request)]
|
||||||
|
|
||||||
def get_row_actions(self, datum):
|
def get_row_actions(self, datum):
|
||||||
""" Returns a list of the action instances for a specific row. """
|
"""Returns a list of the action instances for a specific row."""
|
||||||
bound_actions = []
|
bound_actions = []
|
||||||
for action in self._meta.row_actions:
|
for action in self._meta.row_actions:
|
||||||
# Copy to allow modifying properties per row
|
# Copy to allow modifying properties per row
|
||||||
|
@ -1120,7 +1112,7 @@ class DataTable(object):
|
||||||
return bound_actions
|
return bound_actions
|
||||||
|
|
||||||
def render_table_actions(self):
|
def render_table_actions(self):
|
||||||
""" Renders the actions specified in ``Meta.table_actions``. """
|
"""Renders the actions specified in ``Meta.table_actions``."""
|
||||||
template_path = self._meta.table_actions_template
|
template_path = self._meta.table_actions_template
|
||||||
table_actions_template = template.loader.get_template(template_path)
|
table_actions_template = template.loader.get_template(template_path)
|
||||||
bound_actions = self.get_table_actions()
|
bound_actions = self.get_table_actions()
|
||||||
|
@ -1132,9 +1124,9 @@ class DataTable(object):
|
||||||
return table_actions_template.render(context)
|
return table_actions_template.render(context)
|
||||||
|
|
||||||
def render_row_actions(self, datum):
|
def render_row_actions(self, datum):
|
||||||
|
"""Renders the actions specified in ``Meta.row_actions`` using the
|
||||||
|
current row data.
|
||||||
"""
|
"""
|
||||||
Renders the actions specified in ``Meta.row_actions`` using the
|
|
||||||
current row data. """
|
|
||||||
template_path = self._meta.row_actions_template
|
template_path = self._meta.row_actions_template
|
||||||
row_actions_template = template.loader.get_template(template_path)
|
row_actions_template = template.loader.get_template(template_path)
|
||||||
bound_actions = self.get_row_actions(datum)
|
bound_actions = self.get_row_actions(datum)
|
||||||
|
@ -1145,8 +1137,7 @@ class DataTable(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_action(action_string):
|
def parse_action(action_string):
|
||||||
"""
|
"""Parses the ``action`` parameter (a string) sent back with the
|
||||||
Parses the ``action`` parameter (a string) sent back with the
|
|
||||||
POST data. By default this parses a string formatted as
|
POST data. By default this parses a string formatted as
|
||||||
``{{ table_name }}__{{ action_name }}__{{ row_id }}`` and returns
|
``{{ table_name }}__{{ action_name }}__{{ row_id }}`` and returns
|
||||||
each of the pieces. The ``row_id`` is optional.
|
each of the pieces. The ``row_id`` is optional.
|
||||||
|
@ -1163,8 +1154,7 @@ class DataTable(object):
|
||||||
return table, action, object_id
|
return table, action, object_id
|
||||||
|
|
||||||
def take_action(self, action_name, obj_id=None, obj_ids=None):
|
def take_action(self, action_name, obj_id=None, obj_ids=None):
|
||||||
"""
|
"""Locates the appropriate action and routes the object
|
||||||
Locates the appropriate action and routes the object
|
|
||||||
data to it. The action should return an HTTP redirect
|
data to it. The action should return an HTTP redirect
|
||||||
if successful, or a value which evaluates to ``False``
|
if successful, or a value which evaluates to ``False``
|
||||||
if unsuccessful.
|
if unsuccessful.
|
||||||
|
@ -1200,7 +1190,7 @@ class DataTable(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_handler(cls, request):
|
def check_handler(cls, request):
|
||||||
""" Determine whether the request should be handled by this table. """
|
"""Determine whether the request should be handled by this table."""
|
||||||
if request.method == "POST" and "action" in request.POST:
|
if request.method == "POST" and "action" in request.POST:
|
||||||
table, action, obj_id = cls.parse_action(request.POST["action"])
|
table, action, obj_id = cls.parse_action(request.POST["action"])
|
||||||
elif "table" in request.GET and "action" in request.GET:
|
elif "table" in request.GET and "action" in request.GET:
|
||||||
|
@ -1212,9 +1202,8 @@ class DataTable(object):
|
||||||
return table, action, obj_id
|
return table, action, obj_id
|
||||||
|
|
||||||
def maybe_preempt(self):
|
def maybe_preempt(self):
|
||||||
"""
|
"""Determine whether the request should be handled by a preemptive
|
||||||
Determine whether the request should be handled by a preemptive action
|
action on this table or by an AJAX row update before loading any data.
|
||||||
on this table or by an AJAX row update before loading any data.
|
|
||||||
"""
|
"""
|
||||||
request = self.request
|
request = self.request
|
||||||
table_name, action_name, obj_id = self.check_handler(request)
|
table_name, action_name, obj_id = self.check_handler(request)
|
||||||
|
@ -1247,9 +1236,8 @@ class DataTable(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def maybe_handle(self):
|
def maybe_handle(self):
|
||||||
"""
|
"""Determine whether the request should be handled by any action on
|
||||||
Determine whether the request should be handled by any action on this
|
this table after data has been loaded.
|
||||||
table after data has been loaded.
|
|
||||||
"""
|
"""
|
||||||
request = self.request
|
request = self.request
|
||||||
table_name, action_name, obj_id = self.check_handler(request)
|
table_name, action_name, obj_id = self.check_handler(request)
|
||||||
|
@ -1262,13 +1250,13 @@ class DataTable(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def sanitize_id(self, obj_id):
|
def sanitize_id(self, obj_id):
|
||||||
""" Override to modify an incoming obj_id to match existing
|
"""Override to modify an incoming obj_id to match existing
|
||||||
API data types or modify the format.
|
API data types or modify the format.
|
||||||
"""
|
"""
|
||||||
return obj_id
|
return obj_id
|
||||||
|
|
||||||
def get_object_id(self, datum):
|
def get_object_id(self, datum):
|
||||||
""" Returns the identifier for the object this row will represent.
|
"""Returns the identifier for the object this row will represent.
|
||||||
|
|
||||||
By default this returns an ``id`` attribute on the given object,
|
By default this returns an ``id`` attribute on the given object,
|
||||||
but this can be overridden to return other values.
|
but this can be overridden to return other values.
|
||||||
|
@ -1281,7 +1269,7 @@ class DataTable(object):
|
||||||
return datum.id
|
return datum.id
|
||||||
|
|
||||||
def get_object_display(self, datum):
|
def get_object_display(self, datum):
|
||||||
""" Returns a display name that identifies this object.
|
"""Returns a display name that identifies this object.
|
||||||
|
|
||||||
By default, this returns a ``name`` attribute from the given object,
|
By default, this returns a ``name`` attribute from the given object,
|
||||||
but this can be overriden to return other values.
|
but this can be overriden to return other values.
|
||||||
|
@ -1291,8 +1279,7 @@ class DataTable(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def has_more_data(self):
|
def has_more_data(self):
|
||||||
"""
|
"""Returns a boolean value indicating whether there is more data
|
||||||
Returns a boolean value indicating whether there is more data
|
|
||||||
available to this table from the source (generally an API).
|
available to this table from the source (generally an API).
|
||||||
|
|
||||||
The method is largely meant for internal use, but if you want to
|
The method is largely meant for internal use, but if you want to
|
||||||
|
@ -1301,19 +1288,17 @@ class DataTable(object):
|
||||||
return self._meta.has_more_data
|
return self._meta.has_more_data
|
||||||
|
|
||||||
def get_marker(self):
|
def get_marker(self):
|
||||||
"""
|
"""Returns the identifier for the last object in the current data set
|
||||||
Returns the identifier for the last object in the current data set
|
|
||||||
for APIs that use marker/limit-based paging.
|
for APIs that use marker/limit-based paging.
|
||||||
"""
|
"""
|
||||||
return http.urlquote_plus(self.get_object_id(self.data[-1]))
|
return http.urlquote_plus(self.get_object_id(self.data[-1]))
|
||||||
|
|
||||||
def get_pagination_string(self):
|
def get_pagination_string(self):
|
||||||
""" Returns the query parameter string to paginate this table. """
|
"""Returns the query parameter string to paginate this table."""
|
||||||
return "=".join([self._meta.pagination_param, self.get_marker()])
|
return "=".join([self._meta.pagination_param, self.get_marker()])
|
||||||
|
|
||||||
def calculate_row_status(self, statuses):
|
def calculate_row_status(self, statuses):
|
||||||
"""
|
"""Returns a boolean value determining the overall row status
|
||||||
Returns a boolean value determining the overall row status
|
|
||||||
based on the dictionary of column name to status mappings passed in.
|
based on the dictionary of column name to status mappings passed in.
|
||||||
|
|
||||||
By default, it uses the following logic:
|
By default, it uses the following logic:
|
||||||
|
@ -1339,8 +1324,7 @@ class DataTable(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_row_status_class(self, status):
|
def get_row_status_class(self, status):
|
||||||
"""
|
"""Returns a css class name determined by the status value. This class
|
||||||
Returns a css class name determined by the status value. This class
|
|
||||||
name is used to indicate the status of the rows in the table if
|
name is used to indicate the status of the rows in the table if
|
||||||
any ``status_columns`` have been specified.
|
any ``status_columns`` have been specified.
|
||||||
"""
|
"""
|
||||||
|
@ -1352,11 +1336,11 @@ class DataTable(object):
|
||||||
return "status_unknown"
|
return "status_unknown"
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
""" Returns this table's columns including auto-generated ones."""
|
"""Returns this table's columns including auto-generated ones."""
|
||||||
return self.columns.values()
|
return self.columns.values()
|
||||||
|
|
||||||
def get_rows(self):
|
def get_rows(self):
|
||||||
""" Return the row data for this table broken out by columns. """
|
"""Return the row data for this table broken out by columns."""
|
||||||
rows = []
|
rows = []
|
||||||
try:
|
try:
|
||||||
for datum in self.filtered_data:
|
for datum in self.filtered_data:
|
||||||
|
|
|
@ -22,7 +22,7 @@ from horizon.templatetags.horizon import has_permissions # noqa
|
||||||
|
|
||||||
|
|
||||||
class MultiTableMixin(object):
|
class MultiTableMixin(object):
|
||||||
""" A generic mixin which provides methods for handling DataTables. """
|
"""A generic mixin which provides methods for handling DataTables."""
|
||||||
data_method_pattern = "get_%s_data"
|
data_method_pattern = "get_%s_data"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -123,8 +123,7 @@ class MultiTableMixin(object):
|
||||||
|
|
||||||
|
|
||||||
class MultiTableView(MultiTableMixin, generic.TemplateView):
|
class MultiTableView(MultiTableMixin, generic.TemplateView):
|
||||||
"""
|
"""A class-based generic view to handle the display and processing of
|
||||||
A class-based generic view to handle the display and processing of
|
|
||||||
multiple :class:`~horizon.tables.DataTable` classes in a single view.
|
multiple :class:`~horizon.tables.DataTable` classes in a single view.
|
||||||
|
|
||||||
Three steps are required to use this view: set the ``table_classes``
|
Three steps are required to use this view: set the ``table_classes``
|
||||||
|
@ -164,7 +163,7 @@ class MultiTableView(MultiTableMixin, generic.TemplateView):
|
||||||
|
|
||||||
|
|
||||||
class DataTableView(MultiTableView):
|
class DataTableView(MultiTableView):
|
||||||
""" A class-based generic view to handle basic DataTable processing.
|
"""A class-based generic view to handle basic DataTable processing.
|
||||||
|
|
||||||
Three steps are required to use this view: set the ``table_class``
|
Three steps are required to use this view: set the ``table_class``
|
||||||
attribute with the desired :class:`~horizon.tables.DataTable` class;
|
attribute with the desired :class:`~horizon.tables.DataTable` class;
|
||||||
|
@ -211,7 +210,7 @@ class DataTableView(MultiTableView):
|
||||||
|
|
||||||
|
|
||||||
class MixedDataTableView(DataTableView):
|
class MixedDataTableView(DataTableView):
|
||||||
""" A class-based generic view to handle DataTable with mixed data
|
"""A class-based generic view to handle DataTable with mixed data
|
||||||
types.
|
types.
|
||||||
|
|
||||||
Basic usage is the same as DataTableView.
|
Basic usage is the same as DataTableView.
|
||||||
|
|
|
@ -30,8 +30,7 @@ CSS_DISABLED_TAB_CLASSES = ["disabled"]
|
||||||
|
|
||||||
|
|
||||||
class TabGroup(html.HTMLElement):
|
class TabGroup(html.HTMLElement):
|
||||||
"""
|
"""A container class which knows how to manage and render
|
||||||
A container class which knows how to manage and render
|
|
||||||
:class:`~horizon.tabs.Tab` objects.
|
:class:`~horizon.tabs.Tab` objects.
|
||||||
|
|
||||||
.. attribute:: slug
|
.. attribute:: slug
|
||||||
|
@ -110,9 +109,7 @@ class TabGroup(html.HTMLElement):
|
||||||
return "<%s: %s>" % (self.__class__.__name__, self.slug)
|
return "<%s: %s>" % (self.__class__.__name__, self.slug)
|
||||||
|
|
||||||
def load_tab_data(self):
|
def load_tab_data(self):
|
||||||
"""
|
"""Preload all data that for the tabs that will be displayed."""
|
||||||
Preload all data that for the tabs that will be displayed.
|
|
||||||
"""
|
|
||||||
for tab in self._tabs.values():
|
for tab in self._tabs.values():
|
||||||
if tab.load and not tab.data_loaded:
|
if tab.load and not tab.data_loaded:
|
||||||
try:
|
try:
|
||||||
|
@ -122,15 +119,13 @@ class TabGroup(html.HTMLElement):
|
||||||
exceptions.handle(self.request)
|
exceptions.handle(self.request)
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
"""
|
"""Returns the id for this tab group. Defaults to the value of the tab
|
||||||
Returns the id for this tab group. Defaults to the value of the tab
|
|
||||||
group's :attr:`horizon.tabs.Tab.slug`.
|
group's :attr:`horizon.tabs.Tab.slug`.
|
||||||
"""
|
"""
|
||||||
return self.slug
|
return self.slug
|
||||||
|
|
||||||
def get_default_classes(self):
|
def get_default_classes(self):
|
||||||
"""
|
"""Returns a list of the default classes for the tab group. Defaults to
|
||||||
Returns a list of the default classes for the tab group. Defaults to
|
|
||||||
``["nav", "nav-tabs", "ajax-tabs"]``.
|
``["nav", "nav-tabs", "ajax-tabs"]``.
|
||||||
"""
|
"""
|
||||||
default_classes = super(TabGroup, self).get_default_classes()
|
default_classes = super(TabGroup, self).get_default_classes()
|
||||||
|
@ -138,8 +133,7 @@ class TabGroup(html.HTMLElement):
|
||||||
return default_classes
|
return default_classes
|
||||||
|
|
||||||
def tabs_not_available(self):
|
def tabs_not_available(self):
|
||||||
"""
|
"""In the event that no tabs are either allowed or enabled, this method
|
||||||
In the event that no tabs are either allowed or enabled, this method
|
|
||||||
is the fallback handler. By default it's a no-op, but it exists
|
is the fallback handler. By default it's a no-op, but it exists
|
||||||
to make redirecting or raising exceptions possible for subclasses.
|
to make redirecting or raising exceptions possible for subclasses.
|
||||||
"""
|
"""
|
||||||
|
@ -169,15 +163,15 @@ class TabGroup(html.HTMLElement):
|
||||||
return marked_active
|
return marked_active
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" Renders the HTML output for this tab group. """
|
"""Renders the HTML output for this tab group."""
|
||||||
return render_to_string(self.template_name, {"tab_group": self})
|
return render_to_string(self.template_name, {"tab_group": self})
|
||||||
|
|
||||||
def get_tabs(self):
|
def get_tabs(self):
|
||||||
""" Returns a list of the allowed tabs for this tab group. """
|
"""Returns a list of the allowed tabs for this tab group."""
|
||||||
return filter(lambda tab: tab._allowed, self._tabs.values())
|
return filter(lambda tab: tab._allowed, self._tabs.values())
|
||||||
|
|
||||||
def get_tab(self, tab_name, allow_disabled=False):
|
def get_tab(self, tab_name, allow_disabled=False):
|
||||||
""" Returns a specific tab from this tab group.
|
"""Returns a specific tab from this tab group.
|
||||||
|
|
||||||
If the tab is not allowed or not enabled this method returns ``None``.
|
If the tab is not allowed or not enabled this method returns ``None``.
|
||||||
|
|
||||||
|
@ -193,7 +187,7 @@ class TabGroup(html.HTMLElement):
|
||||||
return filter(lambda t: self.get_tab(t.slug), self._tabs.values())
|
return filter(lambda t: self.get_tab(t.slug), self._tabs.values())
|
||||||
|
|
||||||
def get_selected_tab(self):
|
def get_selected_tab(self):
|
||||||
""" Returns the tab specific by the GET request parameter.
|
"""Returns the tab specific by the GET request parameter.
|
||||||
|
|
||||||
In the event that there is no GET request parameter, the value
|
In the event that there is no GET request parameter, the value
|
||||||
of the query parameter is invalid, or the tab is not allowed/enabled,
|
of the query parameter is invalid, or the tab is not allowed/enabled,
|
||||||
|
@ -208,8 +202,7 @@ class TabGroup(html.HTMLElement):
|
||||||
|
|
||||||
|
|
||||||
class Tab(html.HTMLElement):
|
class Tab(html.HTMLElement):
|
||||||
"""
|
"""A reusable interface for constructing a tab within a
|
||||||
A reusable interface for constructing a tab within a
|
|
||||||
:class:`~horizon.tabs.TabGroup`.
|
:class:`~horizon.tabs.TabGroup`.
|
||||||
|
|
||||||
.. attribute:: name
|
.. attribute:: name
|
||||||
|
@ -265,7 +258,7 @@ class Tab(html.HTMLElement):
|
||||||
return "<%s: %s>" % (self.__class__.__name__, self.slug)
|
return "<%s: %s>" % (self.__class__.__name__, self.slug)
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
""" Method to access whether or not this tab is the active tab. """
|
"""Method to access whether or not this tab is the active tab."""
|
||||||
if self._active is None:
|
if self._active is None:
|
||||||
self.tab_group._set_active_tab()
|
self.tab_group._set_active_tab()
|
||||||
return self._active
|
return self._active
|
||||||
|
@ -286,8 +279,7 @@ class Tab(html.HTMLElement):
|
||||||
return getattr(self, "_data", None) is not None
|
return getattr(self, "_data", None) is not None
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
"""
|
"""Renders the tab to HTML using the
|
||||||
Renders the tab to HTML using the
|
|
||||||
:meth:`~horizon.tabs.Tab.get_context_data` method and
|
:meth:`~horizon.tabs.Tab.get_context_data` method and
|
||||||
the :meth:`~horizon.tabs.Tab.get_template_name` method.
|
the :meth:`~horizon.tabs.Tab.get_template_name` method.
|
||||||
|
|
||||||
|
@ -309,8 +301,7 @@ class Tab(html.HTMLElement):
|
||||||
return render_to_string(self.get_template_name(self.request), context)
|
return render_to_string(self.get_template_name(self.request), context)
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
"""
|
"""Returns the id for this tab. Defaults to
|
||||||
Returns the id for this tab. Defaults to
|
|
||||||
``"{{ tab_group.slug }}__{{ tab.slug }}"``.
|
``"{{ tab_group.slug }}__{{ tab.slug }}"``.
|
||||||
"""
|
"""
|
||||||
return SEPARATOR.join([self.tab_group.slug, self.slug])
|
return SEPARATOR.join([self.tab_group.slug, self.slug])
|
||||||
|
@ -319,8 +310,7 @@ class Tab(html.HTMLElement):
|
||||||
return "=".join((self.tab_group.param_name, self.get_id()))
|
return "=".join((self.tab_group.param_name, self.get_id()))
|
||||||
|
|
||||||
def get_default_classes(self):
|
def get_default_classes(self):
|
||||||
"""
|
"""Returns a list of the default classes for the tab. Defaults to
|
||||||
Returns a list of the default classes for the tab. Defaults to
|
|
||||||
and empty list (``[]``), however additional classes may be added
|
and empty list (``[]``), however additional classes may be added
|
||||||
depending on the state of the tab as follows:
|
depending on the state of the tab as follows:
|
||||||
|
|
||||||
|
@ -338,8 +328,7 @@ class Tab(html.HTMLElement):
|
||||||
return default_classes
|
return default_classes
|
||||||
|
|
||||||
def get_template_name(self, request):
|
def get_template_name(self, request):
|
||||||
"""
|
"""Returns the name of the template to be used for rendering this tab.
|
||||||
Returns the name of the template to be used for rendering this tab.
|
|
||||||
|
|
||||||
By default it returns the value of the ``template_name`` attribute
|
By default it returns the value of the ``template_name`` attribute
|
||||||
on the ``Tab`` class.
|
on the ``Tab`` class.
|
||||||
|
@ -351,16 +340,14 @@ class Tab(html.HTMLElement):
|
||||||
return self.template_name
|
return self.template_name
|
||||||
|
|
||||||
def get_context_data(self, request):
|
def get_context_data(self, request):
|
||||||
"""
|
"""This method should return a dictionary of context data used to
|
||||||
This method should return a dictionary of context data used to render
|
render the tab. Required.
|
||||||
the tab. Required.
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("%s needs to define a get_context_data "
|
raise NotImplementedError("%s needs to define a get_context_data "
|
||||||
"method." % self.__class__.__name__)
|
"method." % self.__class__.__name__)
|
||||||
|
|
||||||
def enabled(self, request):
|
def enabled(self, request):
|
||||||
"""
|
"""Determines whether or not the tab should be accessible
|
||||||
Determines whether or not the tab should be accessible
|
|
||||||
(e.g. be rendered into the HTML on load and respond to a click event).
|
(e.g. be rendered into the HTML on load and respond to a click event).
|
||||||
|
|
||||||
If a tab returns ``False`` from ``enabled`` it will ignore the value
|
If a tab returns ``False`` from ``enabled`` it will ignore the value
|
||||||
|
@ -371,8 +358,7 @@ class Tab(html.HTMLElement):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def allowed(self, request):
|
def allowed(self, request):
|
||||||
"""
|
"""Determines whether or not the tab is displayed.
|
||||||
Determines whether or not the tab is displayed.
|
|
||||||
|
|
||||||
Tab instances can override this method to specify conditions under
|
Tab instances can override this method to specify conditions under
|
||||||
which this tab should not be shown at all by returning ``False``.
|
which this tab should not be shown at all by returning ``False``.
|
||||||
|
@ -382,8 +368,7 @@ class Tab(html.HTMLElement):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""
|
"""Handles POST data sent to a tab.
|
||||||
Handles POST data sent to a tab.
|
|
||||||
|
|
||||||
Tab instances can override this method to have tab-specific POST logic
|
Tab instances can override this method to have tab-specific POST logic
|
||||||
without polluting the TabView code.
|
without polluting the TabView code.
|
||||||
|
@ -394,8 +379,7 @@ class Tab(html.HTMLElement):
|
||||||
|
|
||||||
|
|
||||||
class TableTab(Tab):
|
class TableTab(Tab):
|
||||||
"""
|
"""A :class:`~horizon.tabs.Tab` class which knows how to deal with
|
||||||
A :class:`~horizon.tabs.Tab` class which knows how to deal with
|
|
||||||
:class:`~horizon.tables.DataTable` classes rendered inside of it.
|
:class:`~horizon.tables.DataTable` classes rendered inside of it.
|
||||||
|
|
||||||
This distinct class is required due to the complexity involved in handling
|
This distinct class is required due to the complexity involved in handling
|
||||||
|
@ -427,8 +411,7 @@ class TableTab(Tab):
|
||||||
self._table_data_loaded = False
|
self._table_data_loaded = False
|
||||||
|
|
||||||
def load_table_data(self):
|
def load_table_data(self):
|
||||||
"""
|
"""Calls the ``get_{{ table_name }}_data`` methods for each table class
|
||||||
Calls the ``get_{{ table_name }}_data`` methods for each table class
|
|
||||||
and sets the data on the tables.
|
and sets the data on the tables.
|
||||||
"""
|
"""
|
||||||
# We only want the data to be loaded once, so we track if we have...
|
# We only want the data to be loaded once, so we track if we have...
|
||||||
|
@ -448,8 +431,7 @@ class TableTab(Tab):
|
||||||
self._table_data_loaded = True
|
self._table_data_loaded = True
|
||||||
|
|
||||||
def get_context_data(self, request):
|
def get_context_data(self, request):
|
||||||
"""
|
"""Adds a ``{{ table_name }}_table`` item to the context for each table
|
||||||
Adds a ``{{ table_name }}_table`` item to the context for each table
|
|
||||||
in the :attr:`~horizon.tabs.TableTab.table_classes` attribute.
|
in the :attr:`~horizon.tabs.TableTab.table_classes` attribute.
|
||||||
|
|
||||||
If only one table class is provided, a shortcut ``table`` context
|
If only one table class is provided, a shortcut ``table`` context
|
||||||
|
|
|
@ -19,8 +19,8 @@ from horizon.tabs.base import TableTab # noqa
|
||||||
|
|
||||||
|
|
||||||
class TabView(generic.TemplateView):
|
class TabView(generic.TemplateView):
|
||||||
"""
|
"""A generic class-based view for displaying a
|
||||||
A generic class-based view for displaying a :class:`horizon.tabs.TabGroup`.
|
:class:`horizon.tabs.TabGroup`.
|
||||||
|
|
||||||
This view handles selecting specific tabs and deals with AJAX requests
|
This view handles selecting specific tabs and deals with AJAX requests
|
||||||
gracefully.
|
gracefully.
|
||||||
|
@ -39,13 +39,13 @@ class TabView(generic.TemplateView):
|
||||||
"on %s." % self.__class__.__name__)
|
"on %s." % self.__class__.__name__)
|
||||||
|
|
||||||
def get_tabs(self, request, **kwargs):
|
def get_tabs(self, request, **kwargs):
|
||||||
""" Returns the initialized tab group for this view. """
|
"""Returns the initialized tab group for this view."""
|
||||||
if self._tab_group is None:
|
if self._tab_group is None:
|
||||||
self._tab_group = self.tab_group_class(request, **kwargs)
|
self._tab_group = self.tab_group_class(request, **kwargs)
|
||||||
return self._tab_group
|
return self._tab_group
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
""" Adds the ``tab_group`` variable to the context data. """
|
"""Adds the ``tab_group`` variable to the context data."""
|
||||||
context = super(TabView, self).get_context_data(**kwargs)
|
context = super(TabView, self).get_context_data(**kwargs)
|
||||||
try:
|
try:
|
||||||
tab_group = self.get_tabs(self.request, **kwargs)
|
tab_group = self.get_tabs(self.request, **kwargs)
|
||||||
|
@ -57,8 +57,7 @@ class TabView(generic.TemplateView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def handle_tabbed_response(self, tab_group, context):
|
def handle_tabbed_response(self, tab_group, context):
|
||||||
"""
|
"""Sends back an AJAX-appropriate response for the tab group if
|
||||||
Sends back an AJAX-appropriate response for the tab group if
|
|
||||||
required, otherwise renders the response as normal.
|
required, otherwise renders the response as normal.
|
||||||
"""
|
"""
|
||||||
if self.request.is_ajax():
|
if self.request.is_ajax():
|
||||||
|
@ -90,8 +89,7 @@ class TabbedTableView(tables.MultiTableMixin, TabView):
|
||||||
self._table_dict = {}
|
self._table_dict = {}
|
||||||
|
|
||||||
def load_tabs(self):
|
def load_tabs(self):
|
||||||
"""
|
"""Loads the tab group, and compiles the table instances for each
|
||||||
Loads the tab group, and compiles the table instances for each
|
|
||||||
table attached to any :class:`horizon.tabs.TableTab` instances on
|
table attached to any :class:`horizon.tabs.TableTab` instances on
|
||||||
the tab group. This step is necessary before processing any
|
the tab group. This step is necessary before processing any
|
||||||
tab or table actions.
|
tab or table actions.
|
||||||
|
@ -105,14 +103,13 @@ class TabbedTableView(tables.MultiTableMixin, TabView):
|
||||||
'tab': tab}
|
'tab': tab}
|
||||||
|
|
||||||
def get_tables(self):
|
def get_tables(self):
|
||||||
""" A no-op on this class. Tables are handled at the tab level. """
|
"""A no-op on this class. Tables are handled at the tab level."""
|
||||||
# Override the base class implementation so that the MultiTableMixin
|
# Override the base class implementation so that the MultiTableMixin
|
||||||
# doesn't freak out. We do the processing at the TableTab level.
|
# doesn't freak out. We do the processing at the TableTab level.
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_table(self, table_dict):
|
def handle_table(self, table_dict):
|
||||||
"""
|
"""For the given dict containing a ``DataTable`` and a ``TableTab``
|
||||||
For the given dict containing a ``DataTable`` and a ``TableTab``
|
|
||||||
instance, it loads the table data for that tab and calls the
|
instance, it loads the table data for that tab and calls the
|
||||||
table's :meth:`~horizon.tables.DataTable.maybe_handle` method. The
|
table's :meth:`~horizon.tables.DataTable.maybe_handle` method. The
|
||||||
return value will be the result of ``maybe_handle``.
|
return value will be the result of ``maybe_handle``.
|
||||||
|
|
|
@ -30,8 +30,7 @@ register = template.Library()
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def has_permissions(user, component):
|
def has_permissions(user, component):
|
||||||
"""
|
"""Checks if the given user meets the permissions requirements for
|
||||||
Checks if the given user meets the permissions requirements for
|
|
||||||
the component.
|
the component.
|
||||||
"""
|
"""
|
||||||
return user.has_perms(getattr(component, 'permissions', set()))
|
return user.has_perms(getattr(component, 'permissions', set()))
|
||||||
|
@ -45,7 +44,7 @@ def has_permissions_on_list(components, user):
|
||||||
|
|
||||||
@register.inclusion_tag('horizon/_nav_list.html', takes_context=True)
|
@register.inclusion_tag('horizon/_nav_list.html', takes_context=True)
|
||||||
def horizon_main_nav(context):
|
def horizon_main_nav(context):
|
||||||
""" Generates top-level dashboard navigation entries. """
|
"""Generates top-level dashboard navigation entries."""
|
||||||
if 'request' not in context:
|
if 'request' not in context:
|
||||||
return {}
|
return {}
|
||||||
current_dashboard = context['request'].horizon.get('dashboard', None)
|
current_dashboard = context['request'].horizon.get('dashboard', None)
|
||||||
|
@ -63,7 +62,7 @@ def horizon_main_nav(context):
|
||||||
|
|
||||||
@register.inclusion_tag('horizon/_subnav_list.html', takes_context=True)
|
@register.inclusion_tag('horizon/_subnav_list.html', takes_context=True)
|
||||||
def horizon_dashboard_nav(context):
|
def horizon_dashboard_nav(context):
|
||||||
""" Generates sub-navigation entries for the current dashboard. """
|
"""Generates sub-navigation entries for the current dashboard."""
|
||||||
if 'request' not in context:
|
if 'request' not in context:
|
||||||
return {}
|
return {}
|
||||||
dashboard = context['request'].horizon['dashboard']
|
dashboard = context['request'].horizon['dashboard']
|
||||||
|
@ -97,7 +96,7 @@ def quota(val, units=None):
|
||||||
|
|
||||||
|
|
||||||
class JSTemplateNode(template.Node):
|
class JSTemplateNode(template.Node):
|
||||||
""" Helper node for the ``jstemplate`` template tag. """
|
"""Helper node for the ``jstemplate`` template tag."""
|
||||||
def __init__(self, nodelist):
|
def __init__(self, nodelist):
|
||||||
self.nodelist = nodelist
|
self.nodelist = nodelist
|
||||||
|
|
||||||
|
@ -111,8 +110,7 @@ class JSTemplateNode(template.Node):
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def jstemplate(parser, token):
|
def jstemplate(parser, token):
|
||||||
"""
|
"""Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``,
|
||||||
Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``,
|
|
||||||
``[[`` and ``]]`` with ``{{`` and ``}}`` and
|
``[[`` and ``]]`` with ``{{`` and ``}}`` and
|
||||||
``[%`` and ``%]`` with ``{%`` and ``%}`` to avoid conflicts
|
``[%`` and ``%]`` with ``{%`` and ``%}`` to avoid conflicts
|
||||||
with Django's template engine when using any of the Mustache-based
|
with Django's template engine when using any of the Mustache-based
|
||||||
|
|
|
@ -32,8 +32,8 @@ register = template.Library()
|
||||||
|
|
||||||
class ParseDateNode(template.Node):
|
class ParseDateNode(template.Node):
|
||||||
def render(self, datestring):
|
def render(self, datestring):
|
||||||
"""
|
"""Parses a date-like input string into a timezone aware Python
|
||||||
Parses a date-like input string into a timezone aware Python datetime.
|
datetime.
|
||||||
"""
|
"""
|
||||||
formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f",
|
formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f",
|
||||||
"%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"]
|
"%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"]
|
||||||
|
|
|
@ -73,8 +73,9 @@ def mbformat(mb):
|
||||||
|
|
||||||
@register.filter(name='mb_float_format')
|
@register.filter(name='mb_float_format')
|
||||||
def mb_float_format(mb):
|
def mb_float_format(mb):
|
||||||
""" Takes a size value in mb, and prints returns the data in a
|
"""Takes a size value in mb, and prints returns the data in a
|
||||||
saner unit. """
|
saner unit.
|
||||||
|
"""
|
||||||
if not mb:
|
if not mb:
|
||||||
return 0
|
return 0
|
||||||
return filesizeformat(mb * 1024 * 1024, float_format)
|
return filesizeformat(mb * 1024 * 1024, float_format)
|
||||||
|
|
|
@ -75,8 +75,7 @@ class RequestFactoryWithMessages(RequestFactory):
|
||||||
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
||||||
"The SKIP_UNITTESTS env variable is set.")
|
"The SKIP_UNITTESTS env variable is set.")
|
||||||
class TestCase(django_test.TestCase):
|
class TestCase(django_test.TestCase):
|
||||||
"""
|
"""Specialized base test case class for Horizon which gives access to
|
||||||
Specialized base test case class for Horizon which gives access to
|
|
||||||
numerous additional features:
|
numerous additional features:
|
||||||
|
|
||||||
* The ``mox`` mocking framework via ``self.mox``.
|
* The ``mox`` mocking framework via ``self.mox``.
|
||||||
|
@ -115,15 +114,13 @@ class TestCase(django_test.TestCase):
|
||||||
del self.user._perm_cache
|
del self.user._perm_cache
|
||||||
|
|
||||||
def assertNoMessages(self, response=None):
|
def assertNoMessages(self, response=None):
|
||||||
"""
|
"""Asserts that no messages have been attached by the
|
||||||
Asserts that no messages have been attached by the ``contrib.messages``
|
``contrib.messages`` framework.
|
||||||
framework.
|
|
||||||
"""
|
"""
|
||||||
self.assertMessageCount(response, success=0, warn=0, info=0, error=0)
|
self.assertMessageCount(response, success=0, warn=0, info=0, error=0)
|
||||||
|
|
||||||
def assertMessageCount(self, response=None, **kwargs):
|
def assertMessageCount(self, response=None, **kwargs):
|
||||||
"""
|
"""Asserts that the specified number of messages have been attached
|
||||||
Asserts that the specified number of messages have been attached
|
|
||||||
for various message types. Usage would look like
|
for various message types. Usage would look like
|
||||||
``self.assertMessageCount(success=1)``.
|
``self.assertMessageCount(success=1)``.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -102,12 +102,11 @@ class BaseHorizonTests(test.TestCase):
|
||||||
dash.register(panel)
|
dash.register(panel)
|
||||||
|
|
||||||
def _reload_urls(self):
|
def _reload_urls(self):
|
||||||
'''
|
"""Clears out the URL caches, reloads the root urls module, and
|
||||||
Clears out the URL caches, reloads the root urls module, and
|
|
||||||
re-triggers the autodiscovery mechanism for Horizon. Allows URLs
|
re-triggers the autodiscovery mechanism for Horizon. Allows URLs
|
||||||
to be re-calculated after registering new dashboards. Useful
|
to be re-calculated after registering new dashboards. Useful
|
||||||
only for testing and should never be used on a live site.
|
only for testing and should never be used on a live site.
|
||||||
'''
|
"""
|
||||||
urlresolvers.clear_url_caches()
|
urlresolvers.clear_url_caches()
|
||||||
reload(import_module(settings.ROOT_URLCONF))
|
reload(import_module(settings.ROOT_URLCONF))
|
||||||
base.Horizon._urls()
|
base.Horizon._urls()
|
||||||
|
@ -116,7 +115,7 @@ class BaseHorizonTests(test.TestCase):
|
||||||
class HorizonTests(BaseHorizonTests):
|
class HorizonTests(BaseHorizonTests):
|
||||||
|
|
||||||
def test_registry(self):
|
def test_registry(self):
|
||||||
""" Verify registration and autodiscovery work correctly.
|
"""Verify registration and autodiscovery work correctly.
|
||||||
|
|
||||||
Please note that this implicitly tests that autodiscovery works
|
Please note that this implicitly tests that autodiscovery works
|
||||||
by virtue of the fact that the dashboards listed in
|
by virtue of the fact that the dashboards listed in
|
||||||
|
@ -210,12 +209,12 @@ class HorizonTests(BaseHorizonTests):
|
||||||
reversed(urlpatterns)
|
reversed(urlpatterns)
|
||||||
|
|
||||||
def test_horizon_test_isolation_1(self):
|
def test_horizon_test_isolation_1(self):
|
||||||
""" Isolation Test Part 1: sets a value. """
|
"""Isolation Test Part 1: sets a value."""
|
||||||
cats = horizon.get_dashboard("cats")
|
cats = horizon.get_dashboard("cats")
|
||||||
cats.evil = True
|
cats.evil = True
|
||||||
|
|
||||||
def test_horizon_test_isolation_2(self):
|
def test_horizon_test_isolation_2(self):
|
||||||
""" Isolation Test Part 2: The value set in part 1 should be gone. """
|
"""Isolation Test Part 2: The value set in part 1 should be gone."""
|
||||||
cats = horizon.get_dashboard("cats")
|
cats = horizon.get_dashboard("cats")
|
||||||
self.assertFalse(hasattr(cats, "evil"))
|
self.assertFalse(hasattr(cats, "evil"))
|
||||||
|
|
||||||
|
@ -299,7 +298,7 @@ class HorizonTests(BaseHorizonTests):
|
||||||
|
|
||||||
class CustomPanelTests(BaseHorizonTests):
|
class CustomPanelTests(BaseHorizonTests):
|
||||||
|
|
||||||
""" Test customization of dashboards and panels
|
"""Test customization of dashboards and panels
|
||||||
using 'customization_module' to HORIZON_CONFIG.
|
using 'customization_module' to HORIZON_CONFIG.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -334,7 +333,7 @@ class CustomPanelTests(BaseHorizonTests):
|
||||||
|
|
||||||
class CustomPermissionsTests(BaseHorizonTests):
|
class CustomPermissionsTests(BaseHorizonTests):
|
||||||
|
|
||||||
""" Test customization of permissions on panels
|
"""Test customization of permissions on panels
|
||||||
using 'customization_module' to HORIZON_CONFIG.
|
using 'customization_module' to HORIZON_CONFIG.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ class NoActionsTable(tables.DataTable):
|
||||||
|
|
||||||
class DataTableTests(test.TestCase):
|
class DataTableTests(test.TestCase):
|
||||||
def test_table_instantiation(self):
|
def test_table_instantiation(self):
|
||||||
""" Tests everything that happens when the table is instantiated. """
|
"""Tests everything that happens when the table is instantiated."""
|
||||||
self.table = MyTable(self.request, TEST_DATA)
|
self.table = MyTable(self.request, TEST_DATA)
|
||||||
# Properties defined on the table
|
# Properties defined on the table
|
||||||
self.assertEqual(self.table.data, TEST_DATA)
|
self.assertEqual(self.table.data, TEST_DATA)
|
||||||
|
|
|
@ -29,22 +29,22 @@ from horizon.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
def single_line(text):
|
def single_line(text):
|
||||||
''' Quick utility to make comparing template output easier. '''
|
"""Quick utility to make comparing template output easier."""
|
||||||
return re.sub(' +',
|
return re.sub(' +',
|
||||||
' ',
|
' ',
|
||||||
normalize_newlines(text).replace('\n', '')).strip()
|
normalize_newlines(text).replace('\n', '')).strip()
|
||||||
|
|
||||||
|
|
||||||
class TemplateTagTests(test.TestCase):
|
class TemplateTagTests(test.TestCase):
|
||||||
'''Test Custom Template Tag'''
|
"""Test Custom Template Tag."""
|
||||||
def render_template_tag(self, tag_name, tag_require=''):
|
def render_template_tag(self, tag_name, tag_require=''):
|
||||||
'''Render a Custom Template Tag to string'''
|
"""Render a Custom Template Tag to string."""
|
||||||
template = Template("{%% load %s %%}{%% %s %%}"
|
template = Template("{%% load %s %%}{%% %s %%}"
|
||||||
% (tag_require, tag_name))
|
% (tag_require, tag_name))
|
||||||
return template.render(Context())
|
return template.render(Context())
|
||||||
|
|
||||||
def test_site_branding_tag(self):
|
def test_site_branding_tag(self):
|
||||||
'''Test if site_branding tag renders the correct setting'''
|
"""Test if site_branding tag renders the correct setting."""
|
||||||
rendered_str = self.render_template_tag("site_branding", "branding")
|
rendered_str = self.render_template_tag("site_branding", "branding")
|
||||||
self.assertEqual(settings.SITE_BRANDING, rendered_str.strip(),
|
self.assertEqual(settings.SITE_BRANDING, rendered_str.strip(),
|
||||||
"tag site_branding renders %s" % rendered_str.strip())
|
"tag site_branding renders %s" % rendered_str.strip())
|
||||||
|
|
|
@ -27,8 +27,7 @@ IPv6 = 2
|
||||||
|
|
||||||
|
|
||||||
class IPField(forms.Field):
|
class IPField(forms.Field):
|
||||||
"""
|
"""Form field for entering IP/range values, with validation.
|
||||||
Form field for entering IP/range values, with validation.
|
|
||||||
Supports IPv4/IPv6 in the format:
|
Supports IPv4/IPv6 in the format:
|
||||||
.. xxx.xxx.xxx.xxx
|
.. xxx.xxx.xxx.xxx
|
||||||
.. xxx.xxx.xxx.xxx/zz
|
.. xxx.xxx.xxx.xxx/zz
|
||||||
|
@ -102,9 +101,7 @@ class IPField(forms.Field):
|
||||||
|
|
||||||
|
|
||||||
class MultiIPField(IPField):
|
class MultiIPField(IPField):
|
||||||
"""
|
"""Extends IPField to allow comma-separated lists of addresses."""
|
||||||
Extends IPField to allow comma-separated lists of addresses
|
|
||||||
"""
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
self.addresses = []
|
self.addresses = []
|
||||||
if value:
|
if value:
|
||||||
|
@ -121,8 +118,7 @@ class MultiIPField(IPField):
|
||||||
|
|
||||||
|
|
||||||
class SelectWidget(widgets.Select):
|
class SelectWidget(widgets.Select):
|
||||||
"""
|
"""Customizable select widget, that allows to render
|
||||||
Customizable select widget, that allows to render
|
|
||||||
data-xxx attributes from choices.
|
data-xxx attributes from choices.
|
||||||
|
|
||||||
.. attribute:: data_attrs
|
.. attribute:: data_attrs
|
||||||
|
|
|
@ -29,8 +29,7 @@ def replace_underscores(string):
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def parse_isotime(timestr):
|
def parse_isotime(timestr):
|
||||||
"""
|
"""This duplicates oslo timeutils parse_isotime but with a
|
||||||
This duplicates oslo timeutils parse_isotime but with a
|
|
||||||
@register.filter annotation.
|
@register.filter annotation.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -16,28 +16,25 @@ from django.forms.util import flatatt # noqa
|
||||||
|
|
||||||
|
|
||||||
class HTMLElement(object):
|
class HTMLElement(object):
|
||||||
""" A generic base class that gracefully handles html-style attributes. """
|
"""A generic base class that gracefully handles html-style attributes."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.attrs = getattr(self, "attrs", {})
|
self.attrs = getattr(self, "attrs", {})
|
||||||
self.classes = getattr(self, "classes", [])
|
self.classes = getattr(self, "classes", [])
|
||||||
|
|
||||||
def get_default_classes(self):
|
def get_default_classes(self):
|
||||||
"""
|
"""Returns an iterable of default classes which should be combined with
|
||||||
Returns an iterable of default classes which should be combined with
|
|
||||||
any other declared classes.
|
any other declared classes.
|
||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_default_attrs(self):
|
def get_default_attrs(self):
|
||||||
"""
|
"""Returns a dict of default attributes which should be combined with
|
||||||
Returns a dict of default attributes which should be combined with
|
|
||||||
other declared attributes.
|
other declared attributes.
|
||||||
"""
|
"""
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_final_attrs(self):
|
def get_final_attrs(self):
|
||||||
"""
|
"""Returns a dict containing the final attributes of this element
|
||||||
Returns a dict containing the final attributes of this element
|
|
||||||
which will be rendered.
|
which will be rendered.
|
||||||
"""
|
"""
|
||||||
final_attrs = copy.copy(self.get_default_attrs())
|
final_attrs = copy.copy(self.get_default_attrs())
|
||||||
|
@ -53,16 +50,13 @@ class HTMLElement(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attr_string(self):
|
def attr_string(self):
|
||||||
"""
|
"""Returns a flattened string of HTML attributes based on the
|
||||||
Returns a flattened string of HTML attributes based on the
|
|
||||||
``attrs`` dict provided to the class.
|
``attrs`` dict provided to the class.
|
||||||
"""
|
"""
|
||||||
return flatatt(self.get_final_attrs())
|
return flatatt(self.get_final_attrs())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def class_string(self):
|
def class_string(self):
|
||||||
"""
|
"""Returns a list of class name of HTML Element in string."""
|
||||||
Returns a list of class name of HTML Element in string
|
|
||||||
"""
|
|
||||||
classes_str = " ".join(self.classes)
|
classes_str = " ".join(self.classes)
|
||||||
return classes_str
|
return classes_str
|
||||||
|
|
|
@ -39,7 +39,7 @@ def password_validator_msg():
|
||||||
|
|
||||||
|
|
||||||
def validate_port_or_colon_separated_port_range(port_range):
|
def validate_port_or_colon_separated_port_range(port_range):
|
||||||
"""accepts a port number or a single-colon separated range"""
|
"""Accepts a port number or a single-colon separated range."""
|
||||||
if port_range.count(':') > 1:
|
if port_range.count(':') > 1:
|
||||||
raise ValidationError(_("One colon allowed in port range"))
|
raise ValidationError(_("One colon allowed in port range"))
|
||||||
ports = port_range.split(':')
|
ports = port_range.split(':')
|
||||||
|
|
|
@ -22,12 +22,12 @@ from horizon import exceptions
|
||||||
|
|
||||||
|
|
||||||
def user_home(request):
|
def user_home(request):
|
||||||
""" Reversible named view to direct a user to the appropriate homepage. """
|
"""Reversible named view to direct a user to the appropriate homepage."""
|
||||||
return shortcuts.redirect(horizon.get_user_home(request.user))
|
return shortcuts.redirect(horizon.get_user_home(request.user))
|
||||||
|
|
||||||
|
|
||||||
class APIView(generic.TemplateView):
|
class APIView(generic.TemplateView):
|
||||||
""" A quick class-based view for putting API data into a template.
|
"""A quick class-based view for putting API data into a template.
|
||||||
|
|
||||||
Subclasses must define one method, ``get_data``, and a template name
|
Subclasses must define one method, ``get_data``, and a template name
|
||||||
via the ``template_name`` attribute on the class.
|
via the ``template_name`` attribute on the class.
|
||||||
|
@ -37,8 +37,7 @@ class APIView(generic.TemplateView):
|
||||||
caught.
|
caught.
|
||||||
"""
|
"""
|
||||||
def get_data(self, request, context, *args, **kwargs):
|
def get_data(self, request, context, *args, **kwargs):
|
||||||
"""
|
"""This method should handle any necessary API calls, update the
|
||||||
This method should handle any necessary API calls, update the
|
|
||||||
context object, and return the context object at the end.
|
context object, and return the context object at the end.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("You must define a get_data method "
|
raise NotImplementedError("You must define a get_data method "
|
||||||
|
|
|
@ -76,8 +76,7 @@ class ActionMetaclass(forms.forms.DeclarativeFieldsMetaclass):
|
||||||
|
|
||||||
|
|
||||||
class Action(forms.Form):
|
class Action(forms.Form):
|
||||||
"""
|
"""An ``Action`` represents an atomic logical interaction you can have with
|
||||||
An ``Action`` represents an atomic logical interaction you can have with
|
|
||||||
the system. This is easier to understand with a conceptual example: in the
|
the system. This is easier to understand with a conceptual example: in the
|
||||||
context of a "launch instance" workflow, actions would include "naming
|
context of a "launch instance" workflow, actions would include "naming
|
||||||
the instance", "selecting an image", and ultimately "launching the
|
the instance", "selecting an image", and ultimately "launching the
|
||||||
|
@ -154,7 +153,7 @@ class Action(forms.Form):
|
||||||
bound_field.choices = meth(request, context)
|
bound_field.choices = meth(request, context)
|
||||||
|
|
||||||
def get_help_text(self, extra_context=None):
|
def get_help_text(self, extra_context=None):
|
||||||
""" Returns the help text for this step. """
|
"""Returns the help text for this step."""
|
||||||
text = ""
|
text = ""
|
||||||
extra_context = extra_context or {}
|
extra_context = extra_context or {}
|
||||||
if self.help_text_template:
|
if self.help_text_template:
|
||||||
|
@ -166,14 +165,11 @@ class Action(forms.Form):
|
||||||
return safe(text)
|
return safe(text)
|
||||||
|
|
||||||
def add_error(self, message):
|
def add_error(self, message):
|
||||||
"""
|
"""Adds an error to the Action's Step based on API issues."""
|
||||||
Adds an error to the Action's Step based on API issues.
|
|
||||||
"""
|
|
||||||
self._get_errors()[NON_FIELD_ERRORS] = self.error_class([message])
|
self._get_errors()[NON_FIELD_ERRORS] = self.error_class([message])
|
||||||
|
|
||||||
def handle(self, request, context):
|
def handle(self, request, context):
|
||||||
"""
|
"""Handles any requisite processing for this action. The method should
|
||||||
Handles any requisite processing for this action. The method should
|
|
||||||
return either ``None`` or a dictionary of data to be passed to
|
return either ``None`` or a dictionary of data to be passed to
|
||||||
:meth:`~horizon.workflows.Step.contribute`.
|
:meth:`~horizon.workflows.Step.contribute`.
|
||||||
|
|
||||||
|
@ -183,8 +179,7 @@ class Action(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class MembershipAction(Action):
|
class MembershipAction(Action):
|
||||||
"""
|
"""An action that allows a user to add/remove members from a group.
|
||||||
An action that allows a user to add/remove members from a group.
|
|
||||||
|
|
||||||
Extend the Action class with additional helper method for membership
|
Extend the Action class with additional helper method for membership
|
||||||
management.
|
management.
|
||||||
|
@ -197,8 +192,7 @@ class MembershipAction(Action):
|
||||||
|
|
||||||
|
|
||||||
class Step(object):
|
class Step(object):
|
||||||
"""
|
"""A step is a wrapper around an action which defines its context in a
|
||||||
A step is a wrapper around an action which defines its context in a
|
|
||||||
workflow. It knows about details such as:
|
workflow. It knows about details such as:
|
||||||
|
|
||||||
* The workflow's context data (data passed from step to step).
|
* The workflow's context data (data passed from step to step).
|
||||||
|
@ -380,9 +374,8 @@ class Step(object):
|
||||||
return self._action
|
return self._action
|
||||||
|
|
||||||
def prepare_action_context(self, request, context):
|
def prepare_action_context(self, request, context):
|
||||||
"""
|
"""Allows for customization of how the workflow context is passed to
|
||||||
Allows for customization of how the workflow context is passed to the
|
the action; this is the reverse of what "contribute" does to make the
|
||||||
action; this is the reverse of what "contribute" does to make the
|
|
||||||
action outputs sane for the workflow. Changes to the context are not
|
action outputs sane for the workflow. Changes to the context are not
|
||||||
saved globally here. They are localized to the action.
|
saved globally here. They are localized to the action.
|
||||||
|
|
||||||
|
@ -391,7 +384,7 @@ class Step(object):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
""" Returns the ID for this step. Suitable for use in HTML markup. """
|
"""Returns the ID for this step. Suitable for use in HTML markup."""
|
||||||
return "%s__%s" % (self.workflow.slug, self.slug)
|
return "%s__%s" % (self.workflow.slug, self.slug)
|
||||||
|
|
||||||
def _verify_contributions(self, context):
|
def _verify_contributions(self, context):
|
||||||
|
@ -412,8 +405,7 @@ class Step(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def contribute(self, data, context):
|
def contribute(self, data, context):
|
||||||
"""
|
"""Adds the data listed in ``contributes`` to the workflow's shared
|
||||||
Adds the data listed in ``contributes`` to the workflow's shared
|
|
||||||
context. By default, the context is simply updated with all the data
|
context. By default, the context is simply updated with all the data
|
||||||
returned by the action.
|
returned by the action.
|
||||||
|
|
||||||
|
@ -427,7 +419,7 @@ class Step(object):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" Renders the step. """
|
"""Renders the step."""
|
||||||
step_template = template.loader.get_template(self.template_name)
|
step_template = template.loader.get_template(self.template_name)
|
||||||
extra_context = {"form": self.action,
|
extra_context = {"form": self.action,
|
||||||
"step": self}
|
"step": self}
|
||||||
|
@ -435,21 +427,17 @@ class Step(object):
|
||||||
return step_template.render(context)
|
return step_template.render(context)
|
||||||
|
|
||||||
def get_help_text(self):
|
def get_help_text(self):
|
||||||
""" Returns the help text for this step. """
|
"""Returns the help text for this step."""
|
||||||
text = linebreaks(force_unicode(self.help_text))
|
text = linebreaks(force_unicode(self.help_text))
|
||||||
text += self.action.get_help_text()
|
text += self.action.get_help_text()
|
||||||
return safe(text)
|
return safe(text)
|
||||||
|
|
||||||
def add_error(self, message):
|
def add_error(self, message):
|
||||||
"""
|
"""Adds an error to the Step based on API issues."""
|
||||||
Adds an error to the Step based on API issues.
|
|
||||||
"""
|
|
||||||
self.action.add_error(message)
|
self.action.add_error(message)
|
||||||
|
|
||||||
def has_required_fields(self):
|
def has_required_fields(self):
|
||||||
"""
|
"""Returns True if action contains any required fields."""
|
||||||
Returns True if action contains any required fields
|
|
||||||
"""
|
|
||||||
for key in self.contributes:
|
for key in self.contributes:
|
||||||
field = self.action.fields.get(key, None)
|
field = self.action.fields.get(key, None)
|
||||||
if (field and field.required):
|
if (field and field.required):
|
||||||
|
@ -503,8 +491,7 @@ class UpdateMembersStep(Step):
|
||||||
|
|
||||||
|
|
||||||
class Workflow(html.HTMLElement):
|
class Workflow(html.HTMLElement):
|
||||||
"""
|
"""A Workflow is a collection of Steps. Its interface is very
|
||||||
A Workflow is a collection of Steps. Its interface is very
|
|
||||||
straightforward, but it is responsible for handling some very
|
straightforward, but it is responsible for handling some very
|
||||||
important tasks such as:
|
important tasks such as:
|
||||||
|
|
||||||
|
@ -665,7 +652,7 @@ class Workflow(html.HTMLElement):
|
||||||
return self._ordered_steps
|
return self._ordered_steps
|
||||||
|
|
||||||
def get_step(self, slug):
|
def get_step(self, slug):
|
||||||
""" Returns the instantiated step matching the given slug. """
|
"""Returns the instantiated step matching the given slug."""
|
||||||
for step in self.steps:
|
for step in self.steps:
|
||||||
if step.slug == slug:
|
if step.slug == slug:
|
||||||
return step
|
return step
|
||||||
|
@ -705,8 +692,7 @@ class Workflow(html.HTMLElement):
|
||||||
return steps
|
return steps
|
||||||
|
|
||||||
def get_entry_point(self):
|
def get_entry_point(self):
|
||||||
"""
|
"""Returns the slug of the step which the workflow should begin on.
|
||||||
Returns the slug of the step which the workflow should begin on.
|
|
||||||
|
|
||||||
This method takes into account both already-available data and errors
|
This method takes into account both already-available data and errors
|
||||||
within the steps.
|
within the steps.
|
||||||
|
@ -736,7 +722,7 @@ class Workflow(html.HTMLElement):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls, step_class):
|
def register(cls, step_class):
|
||||||
""" Registers a :class:`~horizon.workflows.Step` with the workflow. """
|
"""Registers a :class:`~horizon.workflows.Step` with the workflow."""
|
||||||
if not inspect.isclass(step_class):
|
if not inspect.isclass(step_class):
|
||||||
raise ValueError('Only classes may be registered.')
|
raise ValueError('Only classes may be registered.')
|
||||||
elif not issubclass(step_class, cls._registerable_class):
|
elif not issubclass(step_class, cls._registerable_class):
|
||||||
|
@ -750,8 +736,7 @@ class Workflow(html.HTMLElement):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unregister(cls, step_class):
|
def unregister(cls, step_class):
|
||||||
"""
|
"""Unregisters a :class:`~horizon.workflows.Step` from the workflow.
|
||||||
Unregisters a :class:`~horizon.workflows.Step` from the workflow.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
cls._cls_registry.remove(step_class)
|
cls._cls_registry.remove(step_class)
|
||||||
|
@ -760,15 +745,13 @@ class Workflow(html.HTMLElement):
|
||||||
return cls._unregister(step_class)
|
return cls._unregister(step_class)
|
||||||
|
|
||||||
def validate(self, context):
|
def validate(self, context):
|
||||||
"""
|
"""Hook for custom context data validation. Should return a boolean
|
||||||
Hook for custom context data validation. Should return a boolean
|
|
||||||
value or raise :class:`~horizon.exceptions.WorkflowValidationError`.
|
value or raise :class:`~horizon.exceptions.WorkflowValidationError`.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""
|
"""Verified that all required data is present in the context and
|
||||||
Verified that all required data is present in the context and
|
|
||||||
calls the ``validate`` method to allow for finer-grained checks
|
calls the ``validate`` method to allow for finer-grained checks
|
||||||
on the context data.
|
on the context data.
|
||||||
"""
|
"""
|
||||||
|
@ -790,8 +773,7 @@ class Workflow(html.HTMLElement):
|
||||||
return self.validate(self.context)
|
return self.validate(self.context)
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
"""
|
"""Finalizes a workflow by running through all the actions in order
|
||||||
Finalizes a workflow by running through all the actions in order
|
|
||||||
and calling their ``handle`` methods. Returns ``True`` on full success,
|
and calling their ``handle`` methods. Returns ``True`` on full success,
|
||||||
or ``False`` for a partial success, e.g. there were non-critical
|
or ``False`` for a partial success, e.g. there were non-critical
|
||||||
errors. (If it failed completely the function wouldn't return.)
|
errors. (If it failed completely the function wouldn't return.)
|
||||||
|
@ -814,15 +796,13 @@ class Workflow(html.HTMLElement):
|
||||||
return not partial
|
return not partial
|
||||||
|
|
||||||
def handle(self, request, context):
|
def handle(self, request, context):
|
||||||
"""
|
"""Handles any final processing for this workflow. Should return a
|
||||||
Handles any final processing for this workflow. Should return a boolean
|
boolean value indicating success.
|
||||||
value indicating success.
|
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
"""
|
"""Returns a URL to redirect the user to upon completion. By default it
|
||||||
Returns a URL to redirect the user to upon completion. By default it
|
|
||||||
will attempt to parse a ``success_url`` attribute on the workflow,
|
will attempt to parse a ``success_url`` attribute on the workflow,
|
||||||
which can take the form of a reversible URL pattern name, or a
|
which can take the form of a reversible URL pattern name, or a
|
||||||
standard HTTP URL.
|
standard HTTP URL.
|
||||||
|
@ -833,8 +813,7 @@ class Workflow(html.HTMLElement):
|
||||||
return self.success_url
|
return self.success_url
|
||||||
|
|
||||||
def format_status_message(self, message):
|
def format_status_message(self, message):
|
||||||
"""
|
"""Hook to allow customization of the message returned to the user
|
||||||
Hook to allow customization of the message returned to the user
|
|
||||||
upon successful or unsuccessful completion of the workflow.
|
upon successful or unsuccessful completion of the workflow.
|
||||||
|
|
||||||
By default it simply inserts the workflow's name into the message
|
By default it simply inserts the workflow's name into the message
|
||||||
|
@ -846,7 +825,7 @@ class Workflow(html.HTMLElement):
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" Renders the workflow. """
|
"""Renders the workflow."""
|
||||||
workflow_template = template.loader.get_template(self.template_name)
|
workflow_template = template.loader.get_template(self.template_name)
|
||||||
extra_context = {"workflow": self}
|
extra_context = {"workflow": self}
|
||||||
if self.request.is_ajax():
|
if self.request.is_ajax():
|
||||||
|
@ -855,7 +834,7 @@ class Workflow(html.HTMLElement):
|
||||||
return workflow_template.render(context)
|
return workflow_template.render(context)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
""" Returns the canonical URL for this workflow.
|
"""Returns the canonical URL for this workflow.
|
||||||
|
|
||||||
This is used for the POST action attribute on the form element
|
This is used for the POST action attribute on the form element
|
||||||
wrapping the workflow.
|
wrapping the workflow.
|
||||||
|
@ -867,8 +846,7 @@ class Workflow(html.HTMLElement):
|
||||||
return self.request.get_full_path().partition('?')[0]
|
return self.request.get_full_path().partition('?')[0]
|
||||||
|
|
||||||
def add_error_to_step(self, message, slug):
|
def add_error_to_step(self, message, slug):
|
||||||
"""
|
"""Adds an error to the workflow's Step with the
|
||||||
Adds an error to the workflow's Step with the
|
|
||||||
specifed slug based on API issues. This is useful
|
specifed slug based on API issues. This is useful
|
||||||
when you wish for API errors to appear as errors on
|
when you wish for API errors to appear as errors on
|
||||||
the form rather than using the messages framework.
|
the form rather than using the messages framework.
|
||||||
|
|
|
@ -27,8 +27,7 @@ from horizon import messages
|
||||||
|
|
||||||
|
|
||||||
class WorkflowView(generic.TemplateView):
|
class WorkflowView(generic.TemplateView):
|
||||||
"""
|
"""A generic class-based view which handles the intricacies of workflow
|
||||||
A generic class-based view which handles the intricacies of workflow
|
|
||||||
processing with minimal user configuration.
|
processing with minimal user configuration.
|
||||||
|
|
||||||
.. attribute:: workflow_class
|
.. attribute:: workflow_class
|
||||||
|
@ -65,14 +64,13 @@ class WorkflowView(generic.TemplateView):
|
||||||
"on %s." % self.__class__.__name__)
|
"on %s." % self.__class__.__name__)
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
"""
|
"""Returns initial data for the workflow. Defaults to using the GET
|
||||||
Returns initial data for the workflow. Defaults to using the GET
|
|
||||||
parameters to allow pre-seeding of the workflow context values.
|
parameters to allow pre-seeding of the workflow context values.
|
||||||
"""
|
"""
|
||||||
return copy.copy(self.request.GET)
|
return copy.copy(self.request.GET)
|
||||||
|
|
||||||
def get_workflow(self):
|
def get_workflow(self):
|
||||||
""" Returns the instanciated workflow class. """
|
"""Returns the instanciated workflow class."""
|
||||||
extra_context = self.get_initial()
|
extra_context = self.get_initial()
|
||||||
entry_point = self.request.GET.get("step", None)
|
entry_point = self.request.GET.get("step", None)
|
||||||
workflow = self.workflow_class(self.request,
|
workflow = self.workflow_class(self.request,
|
||||||
|
@ -81,8 +79,7 @@ class WorkflowView(generic.TemplateView):
|
||||||
return workflow
|
return workflow
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""
|
"""Returns the template context, including the workflow class.
|
||||||
Returns the template context, including the workflow class.
|
|
||||||
|
|
||||||
This method should be overridden in subclasses to provide additional
|
This method should be overridden in subclasses to provide additional
|
||||||
context data to the template.
|
context data to the template.
|
||||||
|
@ -99,7 +96,7 @@ class WorkflowView(generic.TemplateView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
""" Returns the template name to use for this request. """
|
"""Returns the template name to use for this request."""
|
||||||
if self.request.is_ajax():
|
if self.request.is_ajax():
|
||||||
template = self.ajax_template_name
|
template = self.ajax_template_name
|
||||||
else:
|
else:
|
||||||
|
@ -122,13 +119,13 @@ class WorkflowView(generic.TemplateView):
|
||||||
workflow.add_error_to_step(error_msg, step)
|
workflow.add_error_to_step(error_msg, step)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
""" Handler for HTTP GET requests. """
|
"""Handler for HTTP GET requests."""
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
self.set_workflow_step_errors(context)
|
self.set_workflow_step_errors(context)
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
""" Handler for HTTP POST requests. """
|
"""Handler for HTTP POST requests."""
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
workflow = context[self.context_object_name]
|
workflow = context[self.context_object_name]
|
||||||
if workflow.is_valid():
|
if workflow.is_valid():
|
||||||
|
|
|
@ -34,7 +34,7 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class APIVersionManager(object):
|
class APIVersionManager(object):
|
||||||
""" Object to store and manage API versioning data and utility methods. """
|
"""Object to store and manage API versioning data and utility methods."""
|
||||||
|
|
||||||
SETTINGS_KEY = "OPENSTACK_API_VERSIONS"
|
SETTINGS_KEY = "OPENSTACK_API_VERSIONS"
|
||||||
|
|
||||||
|
@ -67,10 +67,10 @@ class APIVersionManager(object):
|
||||||
|
|
||||||
|
|
||||||
class APIResourceWrapper(object):
|
class APIResourceWrapper(object):
|
||||||
""" Simple wrapper for api objects
|
"""Simple wrapper for api objects.
|
||||||
|
|
||||||
Define _attrs on the child class and pass in the
|
Define _attrs on the child class and pass in the
|
||||||
api object as the only argument to the constructor
|
api object as the only argument to the constructor
|
||||||
"""
|
"""
|
||||||
_attrs = []
|
_attrs = []
|
||||||
|
|
||||||
|
@ -97,14 +97,14 @@ class APIResourceWrapper(object):
|
||||||
|
|
||||||
|
|
||||||
class APIDictWrapper(object):
|
class APIDictWrapper(object):
|
||||||
""" Simple wrapper for api dictionaries
|
"""Simple wrapper for api dictionaries
|
||||||
|
|
||||||
Some api calls return dictionaries. This class provides identical
|
Some api calls return dictionaries. This class provides identical
|
||||||
behavior as APIResourceWrapper, except that it will also behave as a
|
behavior as APIResourceWrapper, except that it will also behave as a
|
||||||
dictionary, in addition to attribute accesses.
|
dictionary, in addition to attribute accesses.
|
||||||
|
|
||||||
Attribute access is the preferred method of access, to be
|
Attribute access is the preferred method of access, to be
|
||||||
consistent with api resource objects from novaclient.
|
consistent with api resource objects from novaclient.
|
||||||
"""
|
"""
|
||||||
def __init__(self, apidict):
|
def __init__(self, apidict):
|
||||||
self._apidict = apidict
|
self._apidict = apidict
|
||||||
|
@ -146,8 +146,7 @@ class Quota(object):
|
||||||
|
|
||||||
|
|
||||||
class QuotaSet(Sequence):
|
class QuotaSet(Sequence):
|
||||||
"""
|
"""Wrapper for client QuotaSet objects which turns the individual quotas
|
||||||
Wrapper for client QuotaSet objects which turns the individual quotas
|
|
||||||
into Quota objects for easier handling/iteration.
|
into Quota objects for easier handling/iteration.
|
||||||
|
|
||||||
`QuotaSet` objects support a mix of `list` and `dict` methods; you can use
|
`QuotaSet` objects support a mix of `list` and `dict` methods; you can use
|
||||||
|
@ -177,8 +176,9 @@ class QuotaSet(Sequence):
|
||||||
return self.items[index]
|
return self.items[index]
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
'''Merge another QuotaSet into this one. Existing quotas are
|
"""Merge another QuotaSet into this one. Existing quotas are
|
||||||
not overriden.'''
|
not overriden.
|
||||||
|
"""
|
||||||
if not isinstance(other, QuotaSet):
|
if not isinstance(other, QuotaSet):
|
||||||
msg = "Can only add QuotaSet to QuotaSet, " \
|
msg = "Can only add QuotaSet to QuotaSet, " \
|
||||||
"but received %s instead" % type(other)
|
"but received %s instead" % type(other)
|
||||||
|
|
|
@ -25,8 +25,7 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def is_iterable(var):
|
def is_iterable(var):
|
||||||
""" Return True if the given is list or tuple.
|
"""Return True if the given is list or tuple."""
|
||||||
"""
|
|
||||||
|
|
||||||
return (isinstance(var, (list, tuple)) or
|
return (isinstance(var, (list, tuple)) or
|
||||||
issubclass(var.__class__, (list, tuple)))
|
issubclass(var.__class__, (list, tuple)))
|
||||||
|
@ -34,7 +33,7 @@ def is_iterable(var):
|
||||||
|
|
||||||
def make_query(user_id=None, tenant_id=None, resource_id=None,
|
def make_query(user_id=None, tenant_id=None, resource_id=None,
|
||||||
user_ids=None, tenant_ids=None, resource_ids=None):
|
user_ids=None, tenant_ids=None, resource_ids=None):
|
||||||
""" Returns query built form given parameters.
|
"""Returns query built form given parameters.
|
||||||
|
|
||||||
This query can be then used for querying resources, meters and
|
This query can be then used for querying resources, meters and
|
||||||
statistics.
|
statistics.
|
||||||
|
@ -71,15 +70,13 @@ def make_query(user_id=None, tenant_id=None, resource_id=None,
|
||||||
|
|
||||||
|
|
||||||
class Meter(base.APIResourceWrapper):
|
class Meter(base.APIResourceWrapper):
|
||||||
""" Represents one Ceilometer meter.
|
"""Represents one Ceilometer meter."""
|
||||||
"""
|
|
||||||
_attrs = ['name', 'type', 'unit', 'resource_id', 'user_id',
|
_attrs = ['name', 'type', 'unit', 'resource_id', 'user_id',
|
||||||
'project_id']
|
'project_id']
|
||||||
|
|
||||||
|
|
||||||
class Resource(base.APIResourceWrapper):
|
class Resource(base.APIResourceWrapper):
|
||||||
""" Represents one Ceilometer resource.
|
"""Represents one Ceilometer resource."""
|
||||||
"""
|
|
||||||
_attrs = ['resource_id', 'source', 'user_id', 'project_id', 'metadata',
|
_attrs = ['resource_id', 'source', 'user_id', 'project_id', 'metadata',
|
||||||
'links']
|
'links']
|
||||||
|
|
||||||
|
@ -140,7 +137,7 @@ class Resource(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class ResourceAggregate(Resource):
|
class ResourceAggregate(Resource):
|
||||||
""" Represents aggregate of more resources together.
|
"""Represents aggregate of more resources together.
|
||||||
|
|
||||||
Aggregate of resources can be obtain by specifing
|
Aggregate of resources can be obtain by specifing
|
||||||
multiple ids in one parameter or by not specifying
|
multiple ids in one parameter or by not specifying
|
||||||
|
@ -194,8 +191,7 @@ class ResourceAggregate(Resource):
|
||||||
|
|
||||||
|
|
||||||
class Sample(base.APIResourceWrapper):
|
class Sample(base.APIResourceWrapper):
|
||||||
""" Represents one Ceilometer sample.
|
"""Represents one Ceilometer sample."""
|
||||||
"""
|
|
||||||
|
|
||||||
_attrs = ['counter_name', 'user_id', 'resource_id', 'timestamp',
|
_attrs = ['counter_name', 'user_id', 'resource_id', 'timestamp',
|
||||||
'resource_metadata', 'source', 'counter_unit', 'counter_volume',
|
'resource_metadata', 'source', 'counter_unit', 'counter_volume',
|
||||||
|
@ -215,8 +211,7 @@ class Sample(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Statistic(base.APIResourceWrapper):
|
class Statistic(base.APIResourceWrapper):
|
||||||
""" Represents one Ceilometer statistic.
|
"""Represents one Ceilometer statistic."""
|
||||||
"""
|
|
||||||
|
|
||||||
_attrs = ['period', 'period_start', 'period_end',
|
_attrs = ['period', 'period_start', 'period_end',
|
||||||
'count', 'min', 'max', 'sum', 'avg',
|
'count', 'min', 'max', 'sum', 'avg',
|
||||||
|
@ -224,11 +219,11 @@ class Statistic(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class GlobalDiskUsage(base.APIResourceWrapper):
|
class GlobalDiskUsage(base.APIResourceWrapper):
|
||||||
""" Represents collection of resources with statistic of defined meters.
|
"""Represents collection of resources with statistic of defined meters.
|
||||||
|
|
||||||
Resources are filtered either by given default query or by
|
Resources are filtered either by given default query or by
|
||||||
meters in python. It's preferred to use default_query as it is more
|
meters in python. It's preferred to use default_query as it is more
|
||||||
effective.
|
effective.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_attrs = ["id", "tenant", "user", "resource", "disk_read_bytes",
|
_attrs = ["id", "tenant", "user", "resource", "disk_read_bytes",
|
||||||
|
@ -247,11 +242,11 @@ class GlobalDiskUsage(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class GlobalNetworkTrafficUsage(base.APIResourceWrapper):
|
class GlobalNetworkTrafficUsage(base.APIResourceWrapper):
|
||||||
""" Represents collection of resources with statistic of defined meters.
|
"""Represents collection of resources with statistic of defined meters.
|
||||||
|
|
||||||
Resources are filtered either by given default query or by
|
Resources are filtered either by given default query or by
|
||||||
meters in python. It's preferred to use default_query as it is more
|
meters in python. It's preferred to use default_query as it is more
|
||||||
effective.
|
effective.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_attrs = ["id", "tenant", "user", "resource", "network_incoming_bytes",
|
_attrs = ["id", "tenant", "user", "resource", "network_incoming_bytes",
|
||||||
|
@ -270,11 +265,11 @@ class GlobalNetworkTrafficUsage(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class GlobalNetworkUsage(base.APIResourceWrapper):
|
class GlobalNetworkUsage(base.APIResourceWrapper):
|
||||||
""" Represents collection of resources with statistic of defined meters.
|
"""Represents collection of resources with statistic of defined meters.
|
||||||
|
|
||||||
Resources are filtered either by given default query or by
|
Resources are filtered either by given default query or by
|
||||||
meters in python. It's preferred to use default_query as it is more
|
meters in python. It's preferred to use default_query as it is more
|
||||||
effective.
|
effective.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_attrs = ["id", "tenant", "user", "resource", "network", "network_create",
|
_attrs = ["id", "tenant", "user", "resource", "network", "network_create",
|
||||||
|
@ -294,11 +289,11 @@ class GlobalNetworkUsage(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class GlobalObjectStoreUsage(base.APIResourceWrapper):
|
class GlobalObjectStoreUsage(base.APIResourceWrapper):
|
||||||
""" Represents collection of resources with statistic of defined meters.
|
"""Represents collection of resources with statistic of defined meters.
|
||||||
|
|
||||||
Resources are filtered either by given default query or by
|
Resources are filtered either by given default query or by
|
||||||
meters in python. It's preferred to use default_query as it is more
|
meters in python. It's preferred to use default_query as it is more
|
||||||
effective.
|
effective.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_attrs = ["id", "tenant", "user", "resource", "storage_objects",
|
_attrs = ["id", "tenant", "user", "resource", "storage_objects",
|
||||||
|
@ -317,8 +312,7 @@ class GlobalObjectStoreUsage(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
def ceilometerclient(request):
|
def ceilometerclient(request):
|
||||||
""" Initialization of Ceilometer client.
|
"""Initialization of Ceilometer client."""
|
||||||
"""
|
|
||||||
|
|
||||||
endpoint = base.url_for(request, 'metering')
|
endpoint = base.url_for(request, 'metering')
|
||||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||||
|
@ -359,8 +353,8 @@ def statistic_list(request, meter_name, query=None, period=None):
|
||||||
|
|
||||||
|
|
||||||
class ThreadedUpdateResourceWithStatistics(threading.Thread):
|
class ThreadedUpdateResourceWithStatistics(threading.Thread):
|
||||||
""" Multithread wrapper for update_with_statistics method of
|
"""Multithread wrapper for update_with_statistics method of
|
||||||
resource_usage.
|
resource_usage.
|
||||||
|
|
||||||
A join logic is placed in process_list class method. All resources
|
A join logic is placed in process_list class method. All resources
|
||||||
will have its statistics attribute filled in separate threads.
|
will have its statistics attribute filled in separate threads.
|
||||||
|
@ -426,7 +420,7 @@ class ThreadedUpdateResourceWithStatistics(threading.Thread):
|
||||||
|
|
||||||
|
|
||||||
class CeilometerUsage(object):
|
class CeilometerUsage(object):
|
||||||
""" Represents wrapper of any Ceilometer queries.
|
"""Represents wrapper of any Ceilometer queries.
|
||||||
|
|
||||||
One instance of this class should be shared between resources
|
One instance of this class should be shared between resources
|
||||||
as this class provides a place where users and tenants are
|
as this class provides a place where users and tenants are
|
||||||
|
@ -448,7 +442,7 @@ class CeilometerUsage(object):
|
||||||
self._tenants = {}
|
self._tenants = {}
|
||||||
|
|
||||||
def get_user(self, user_id):
|
def get_user(self, user_id):
|
||||||
""" Returns user fetched form API
|
"""Returns user fetched form API
|
||||||
|
|
||||||
Caching the result, so it doesn't contact API twice with the
|
Caching the result, so it doesn't contact API twice with the
|
||||||
same query
|
same query
|
||||||
|
@ -462,7 +456,7 @@ class CeilometerUsage(object):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def preload_all_users(self):
|
def preload_all_users(self):
|
||||||
""" Preloads all users into dictionary.
|
"""Preloads all users into dictionary.
|
||||||
|
|
||||||
It's more effective to preload all users, rather the fetching many
|
It's more effective to preload all users, rather the fetching many
|
||||||
users by separate API get calls.
|
users by separate API get calls.
|
||||||
|
@ -475,7 +469,7 @@ class CeilometerUsage(object):
|
||||||
self._users[u.id] = u
|
self._users[u.id] = u
|
||||||
|
|
||||||
def get_tenant(self, tenant_id):
|
def get_tenant(self, tenant_id):
|
||||||
""" Returns tenant fetched form API
|
"""Returns tenant fetched form API.
|
||||||
|
|
||||||
Caching the result, so it doesn't contact API twice with the
|
Caching the result, so it doesn't contact API twice with the
|
||||||
same query
|
same query
|
||||||
|
@ -489,7 +483,7 @@ class CeilometerUsage(object):
|
||||||
return tenant
|
return tenant
|
||||||
|
|
||||||
def preload_all_tenants(self):
|
def preload_all_tenants(self):
|
||||||
""" Preloads all teannts into dictionary.
|
"""Preloads all teannts into dictionary.
|
||||||
|
|
||||||
It's more effective to preload all tenants, rather the fetching many
|
It's more effective to preload all tenants, rather the fetching many
|
||||||
tenants by separate API get calls.
|
tenants by separate API get calls.
|
||||||
|
@ -504,7 +498,7 @@ class CeilometerUsage(object):
|
||||||
def global_data_get(self, used_cls=None, query=None,
|
def global_data_get(self, used_cls=None, query=None,
|
||||||
with_statistics=False, additional_query=None,
|
with_statistics=False, additional_query=None,
|
||||||
with_users_and_tenants=True):
|
with_users_and_tenants=True):
|
||||||
""" Obtaining a resources for table view.
|
"""Obtaining a resources for table view.
|
||||||
|
|
||||||
It obtains resources with statistics data according to declaration
|
It obtains resources with statistics data according to declaration
|
||||||
in used_cls class.
|
in used_cls class.
|
||||||
|
@ -534,10 +528,9 @@ class CeilometerUsage(object):
|
||||||
filter_func = None
|
filter_func = None
|
||||||
|
|
||||||
def filter_resources(resource):
|
def filter_resources(resource):
|
||||||
""" Method for filtering resources by theirs links.rel attr.
|
"""Method for filtering resources by theirs links.rel attr.
|
||||||
|
|
||||||
The links.rel attributes contains all meters the resource
|
The links.rel attributes contains all meters the resource have.
|
||||||
have.
|
|
||||||
"""
|
"""
|
||||||
for link in resource.links:
|
for link in resource.links:
|
||||||
if link['rel'] in used_cls.meters:
|
if link['rel'] in used_cls.meters:
|
||||||
|
@ -567,8 +560,7 @@ class CeilometerUsage(object):
|
||||||
|
|
||||||
def global_disk_usage(self, query=None, with_statistics=False,
|
def global_disk_usage(self, query=None, with_statistics=False,
|
||||||
additional_query=None):
|
additional_query=None):
|
||||||
""" Wrapper for specific call of global_data_get.
|
"""Wrapper for specific call of global_data_get."""
|
||||||
"""
|
|
||||||
|
|
||||||
return self.global_data_get(used_cls=GlobalDiskUsage,
|
return self.global_data_get(used_cls=GlobalDiskUsage,
|
||||||
query=query,
|
query=query,
|
||||||
|
@ -577,7 +569,7 @@ class CeilometerUsage(object):
|
||||||
|
|
||||||
def global_network_traffic_usage(self, query=None, with_statistics=False,
|
def global_network_traffic_usage(self, query=None, with_statistics=False,
|
||||||
additional_query=None):
|
additional_query=None):
|
||||||
""" Wrapper for specific call of global_data_get.
|
"""Wrapper for specific call of global_data_get.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.global_data_get(used_cls=GlobalNetworkTrafficUsage,
|
return self.global_data_get(used_cls=GlobalNetworkTrafficUsage,
|
||||||
|
@ -587,7 +579,7 @@ class CeilometerUsage(object):
|
||||||
|
|
||||||
def global_network_usage(self, query=None, with_statistics=False,
|
def global_network_usage(self, query=None, with_statistics=False,
|
||||||
additional_query=None):
|
additional_query=None):
|
||||||
""" Wrapper for specific call of global_data_get.
|
"""Wrapper for specific call of global_data_get.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.global_data_get(used_cls=GlobalNetworkUsage,
|
return self.global_data_get(used_cls=GlobalNetworkUsage,
|
||||||
|
@ -597,7 +589,7 @@ class CeilometerUsage(object):
|
||||||
|
|
||||||
def global_object_store_usage(self, query=None, with_statistics=False,
|
def global_object_store_usage(self, query=None, with_statistics=False,
|
||||||
additional_query=None):
|
additional_query=None):
|
||||||
""" Wrapper for specific call of global_data_get.
|
"""Wrapper for specific call of global_data_get.
|
||||||
"""
|
"""
|
||||||
# TODO(lsmola) This call uses all tenants now. When ajax pagination
|
# TODO(lsmola) This call uses all tenants now. When ajax pagination
|
||||||
# is added, it needs to obtain only paginated tenants.
|
# is added, it needs to obtain only paginated tenants.
|
||||||
|
@ -609,7 +601,7 @@ class CeilometerUsage(object):
|
||||||
additional_query=additional_query)
|
additional_query=additional_query)
|
||||||
|
|
||||||
def query_from_object_id(self, object_id):
|
def query_from_object_id(self, object_id):
|
||||||
""" Obtaining a query from resource id.
|
"""Obtaining a query from resource id.
|
||||||
|
|
||||||
Query can be then used to identify a resource in resources or meters
|
Query can be then used to identify a resource in resources or meters
|
||||||
API calls. ID is being built in the Resource initializer, or returned
|
API calls. ID is being built in the Resource initializer, or returned
|
||||||
|
@ -625,7 +617,7 @@ class CeilometerUsage(object):
|
||||||
|
|
||||||
def update_with_statistics(self, resource, meter_names=None, period=None,
|
def update_with_statistics(self, resource, meter_names=None, period=None,
|
||||||
stats_attr=None, additional_query=None):
|
stats_attr=None, additional_query=None):
|
||||||
""" Adding statistical data into one Resource or ResourceAggregate.
|
"""Adding statistical data into one Resource or ResourceAggregate.
|
||||||
|
|
||||||
It adds each statistic of each meter_names into the resource
|
It adds each statistic of each meter_names into the resource
|
||||||
attributes. Attribute name is the meter name with replaced '.' to '_'.
|
attributes. Attribute name is the meter name with replaced '.' to '_'.
|
||||||
|
@ -684,7 +676,7 @@ class CeilometerUsage(object):
|
||||||
|
|
||||||
def resources(self, query=None, filter_func=None,
|
def resources(self, query=None, filter_func=None,
|
||||||
with_users_and_tenants=False):
|
with_users_and_tenants=False):
|
||||||
""" Obtaining resources with the query or filter_func.
|
"""Obtaining resources with the query or filter_func.
|
||||||
|
|
||||||
Obtains resources and also fetch tenants and users associated
|
Obtains resources and also fetch tenants and users associated
|
||||||
with those resources if with_users_and_tenants flag is true.
|
with those resources if with_users_and_tenants flag is true.
|
||||||
|
@ -712,7 +704,7 @@ class CeilometerUsage(object):
|
||||||
period=None, filter_func=None,
|
period=None, filter_func=None,
|
||||||
stats_attr=None, additional_query=None,
|
stats_attr=None, additional_query=None,
|
||||||
with_users_and_tenants=False):
|
with_users_and_tenants=False):
|
||||||
""" Obtaining resources with statistics data inside.
|
"""Obtaining resources with statistics data inside.
|
||||||
|
|
||||||
:Parameters:
|
:Parameters:
|
||||||
- `query`: Query for fetching the Ceilometer Resources.
|
- `query`: Query for fetching the Ceilometer Resources.
|
||||||
|
@ -745,7 +737,7 @@ class CeilometerUsage(object):
|
||||||
return resources
|
return resources
|
||||||
|
|
||||||
def resource_aggregates(self, queries=None):
|
def resource_aggregates(self, queries=None):
|
||||||
""" Obtaining resource aggregates with queries.
|
"""Obtaining resource aggregates with queries.
|
||||||
|
|
||||||
Representing a resource aggregate by query is a most general way
|
Representing a resource aggregate by query is a most general way
|
||||||
how to obtain a resource aggregates.
|
how to obtain a resource aggregates.
|
||||||
|
@ -764,7 +756,7 @@ class CeilometerUsage(object):
|
||||||
def resource_aggregates_with_statistics(self, queries=None,
|
def resource_aggregates_with_statistics(self, queries=None,
|
||||||
meter_names=None, period=None, filter_func=None, stats_attr=None,
|
meter_names=None, period=None, filter_func=None, stats_attr=None,
|
||||||
additional_query=None):
|
additional_query=None):
|
||||||
""" Obtaining resource aggregates with statistics data inside.
|
"""Obtaining resource aggregates with statistics data inside.
|
||||||
|
|
||||||
:Parameters:
|
:Parameters:
|
||||||
- `queries`: Dictionary of named queries that defines a bulk of
|
- `queries`: Dictionary of named queries that defines a bulk of
|
||||||
|
|
|
@ -68,8 +68,7 @@ def cinderclient(request):
|
||||||
|
|
||||||
|
|
||||||
def volume_list(request, search_opts=None):
|
def volume_list(request, search_opts=None):
|
||||||
"""
|
"""To see all volumes in the cloud as an admin you can pass in a special
|
||||||
To see all volumes in the cloud as an admin you can pass in a special
|
|
||||||
search option: {'all_tenants': 1}
|
search option: {'all_tenants': 1}
|
||||||
"""
|
"""
|
||||||
c_client = cinderclient(request)
|
c_client = cinderclient(request)
|
||||||
|
@ -181,8 +180,7 @@ def list_extensions(request):
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def extension_supported(request, extension_name):
|
def extension_supported(request, extension_name):
|
||||||
"""
|
"""This method will determine if Cinder supports a given extension name.
|
||||||
This method will determine if Cinder supports a given extension name.
|
|
||||||
"""
|
"""
|
||||||
extensions = list_extensions(request)
|
extensions = list_extensions(request)
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
|
|
|
@ -24,7 +24,7 @@ neutronclient = neutron.neutronclient
|
||||||
|
|
||||||
|
|
||||||
class Rule(neutron.NeutronAPIDictWrapper):
|
class Rule(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron firewall rule"""
|
"""Wrapper for neutron firewall rule."""
|
||||||
|
|
||||||
def get_dict(self):
|
def get_dict(self):
|
||||||
rule_dict = self._apidict
|
rule_dict = self._apidict
|
||||||
|
@ -33,7 +33,7 @@ class Rule(neutron.NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Policy(neutron.NeutronAPIDictWrapper):
|
class Policy(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron firewall policy"""
|
"""Wrapper for neutron firewall policy."""
|
||||||
|
|
||||||
def get_dict(self):
|
def get_dict(self):
|
||||||
policy_dict = self._apidict
|
policy_dict = self._apidict
|
||||||
|
@ -42,7 +42,7 @@ class Policy(neutron.NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Firewall(neutron.NeutronAPIDictWrapper):
|
class Firewall(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron firewall"""
|
"""Wrapper for neutron firewall."""
|
||||||
|
|
||||||
def get_dict(self):
|
def get_dict(self):
|
||||||
firewall_dict = self._apidict
|
firewall_dict = self._apidict
|
||||||
|
|
|
@ -51,8 +51,7 @@ def image_delete(request, image_id):
|
||||||
|
|
||||||
|
|
||||||
def image_get(request, image_id):
|
def image_get(request, image_id):
|
||||||
"""
|
"""Returns an Image object populated with metadata for image
|
||||||
Returns an Image object populated with metadata for image
|
|
||||||
with supplied identifier.
|
with supplied identifier.
|
||||||
"""
|
"""
|
||||||
return glanceclient(request).images.get(image_id)
|
return glanceclient(request).images.get(image_id)
|
||||||
|
|
|
@ -74,7 +74,7 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class Service(base.APIDictWrapper):
|
class Service(base.APIDictWrapper):
|
||||||
""" Wrapper for a dict based on the service data from keystone. """
|
"""Wrapper for a dict based on the service data from keystone."""
|
||||||
_attrs = ['id', 'type', 'name']
|
_attrs = ['id', 'type', 'name']
|
||||||
|
|
||||||
def __init__(self, service, region, *args, **kwargs):
|
def __init__(self, service, region, *args, **kwargs):
|
||||||
|
@ -216,8 +216,7 @@ def tenant_create(request, name, description=None, enabled=None, domain=None):
|
||||||
|
|
||||||
|
|
||||||
def get_default_domain(request):
|
def get_default_domain(request):
|
||||||
"""
|
"""Gets the default domain object to use when creating Identity object.
|
||||||
Gets the default domain object to use when creating Identity object.
|
|
||||||
Returns the domain context if is set, otherwise return the domain
|
Returns the domain context if is set, otherwise return the domain
|
||||||
of the logon user.
|
of the logon user.
|
||||||
"""
|
"""
|
||||||
|
@ -482,7 +481,7 @@ def role_delete(request, role_id):
|
||||||
|
|
||||||
|
|
||||||
def role_list(request):
|
def role_list(request):
|
||||||
""" Returns a global list of available roles. """
|
"""Returns a global list of available roles."""
|
||||||
return keystoneclient(request, admin=True).roles.list()
|
return keystoneclient(request, admin=True).roles.list()
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,7 +495,7 @@ def roles_for_user(request, user, project):
|
||||||
|
|
||||||
def add_tenant_user_role(request, project=None, user=None, role=None,
|
def add_tenant_user_role(request, project=None, user=None, role=None,
|
||||||
group=None, domain=None):
|
group=None, domain=None):
|
||||||
""" Adds a role for a user on a tenant. """
|
"""Adds a role for a user on a tenant."""
|
||||||
manager = keystoneclient(request, admin=True).roles
|
manager = keystoneclient(request, admin=True).roles
|
||||||
if VERSIONS.active < 3:
|
if VERSIONS.active < 3:
|
||||||
return manager.add_user_role(user, role, project)
|
return manager.add_user_role(user, role, project)
|
||||||
|
@ -507,7 +506,7 @@ def add_tenant_user_role(request, project=None, user=None, role=None,
|
||||||
|
|
||||||
def remove_tenant_user_role(request, project=None, user=None, role=None,
|
def remove_tenant_user_role(request, project=None, user=None, role=None,
|
||||||
group=None, domain=None):
|
group=None, domain=None):
|
||||||
""" Removes a given single role for a user from a tenant. """
|
"""Removes a given single role for a user from a tenant."""
|
||||||
manager = keystoneclient(request, admin=True).roles
|
manager = keystoneclient(request, admin=True).roles
|
||||||
if VERSIONS.active < 3:
|
if VERSIONS.active < 3:
|
||||||
return manager.remove_user_role(user, role, project)
|
return manager.remove_user_role(user, role, project)
|
||||||
|
@ -517,7 +516,7 @@ def remove_tenant_user_role(request, project=None, user=None, role=None,
|
||||||
|
|
||||||
|
|
||||||
def remove_tenant_user(request, project=None, user=None, domain=None):
|
def remove_tenant_user(request, project=None, user=None, domain=None):
|
||||||
""" Removes all roles from a user on a tenant, removing them from it. """
|
"""Removes all roles from a user on a tenant, removing them from it."""
|
||||||
client = keystoneclient(request, admin=True)
|
client = keystoneclient(request, admin=True)
|
||||||
roles = client.roles.roles_for_user(user, project)
|
roles = client.roles.roles_for_user(user, project)
|
||||||
for role in roles:
|
for role in roles:
|
||||||
|
@ -531,22 +530,22 @@ def roles_for_group(request, group, domain=None, project=None):
|
||||||
|
|
||||||
|
|
||||||
def add_group_role(request, role, group, domain=None, project=None):
|
def add_group_role(request, role, group, domain=None, project=None):
|
||||||
""" Adds a role for a group on a domain or project ."""
|
"""Adds a role for a group on a domain or project."""
|
||||||
manager = keystoneclient(request, admin=True).roles
|
manager = keystoneclient(request, admin=True).roles
|
||||||
return manager.grant(role=role, group=group, domain=domain,
|
return manager.grant(role=role, group=group, domain=domain,
|
||||||
project=project)
|
project=project)
|
||||||
|
|
||||||
|
|
||||||
def remove_group_role(request, role, group, domain=None, project=None):
|
def remove_group_role(request, role, group, domain=None, project=None):
|
||||||
""" Removes a given single role for a group from a domain or project. """
|
"""Removes a given single role for a group from a domain or project."""
|
||||||
manager = keystoneclient(request, admin=True).roles
|
manager = keystoneclient(request, admin=True).roles
|
||||||
return manager.revoke(role=role, group=group, project=project,
|
return manager.revoke(role=role, group=group, project=project,
|
||||||
domain=domain)
|
domain=domain)
|
||||||
|
|
||||||
|
|
||||||
def remove_group_roles(request, group, domain=None, project=None):
|
def remove_group_roles(request, group, domain=None, project=None):
|
||||||
""" Removes all roles from a group on a domain or project,
|
"""Removes all roles from a group on a domain or project,
|
||||||
removing them from it.
|
removing them from it.
|
||||||
"""
|
"""
|
||||||
client = keystoneclient(request, admin=True)
|
client = keystoneclient(request, admin=True)
|
||||||
roles = client.roles.list(group=group, domain=domain, project=project)
|
roles = client.roles.list(group=group, domain=domain, project=project)
|
||||||
|
@ -556,8 +555,7 @@ def remove_group_roles(request, group, domain=None, project=None):
|
||||||
|
|
||||||
|
|
||||||
def get_default_role(request):
|
def get_default_role(request):
|
||||||
"""
|
"""Gets the default role object from Keystone and saves it as a global
|
||||||
Gets the default role object from Keystone and saves it as a global
|
|
||||||
since this is configured in settings and should not change from request
|
since this is configured in settings and should not change from request
|
||||||
to request. Supports lookup by name or id.
|
to request. Supports lookup by name or id.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -22,14 +22,14 @@ neutronclient = neutron.neutronclient
|
||||||
|
|
||||||
|
|
||||||
class Vip(neutron.NeutronAPIDictWrapper):
|
class Vip(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron load balancer vip"""
|
"""Wrapper for neutron load balancer vip."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
super(Vip, self).__init__(apiresource)
|
super(Vip, self).__init__(apiresource)
|
||||||
|
|
||||||
|
|
||||||
class Pool(neutron.NeutronAPIDictWrapper):
|
class Pool(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron load balancer pool"""
|
"""Wrapper for neutron load balancer pool."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
if 'provider' not in apiresource:
|
if 'provider' not in apiresource:
|
||||||
|
@ -75,7 +75,7 @@ class Pool(neutron.NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Member(neutron.NeutronAPIDictWrapper):
|
class Member(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron load balancer member"""
|
"""Wrapper for neutron load balancer member."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
super(Member, self).__init__(apiresource)
|
super(Member, self).__init__(apiresource)
|
||||||
|
@ -104,14 +104,14 @@ class Member(neutron.NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class PoolStats(neutron.NeutronAPIDictWrapper):
|
class PoolStats(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron load balancer pool stats"""
|
"""Wrapper for neutron load balancer pool stats."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
super(PoolStats, self).__init__(apiresource)
|
super(PoolStats, self).__init__(apiresource)
|
||||||
|
|
||||||
|
|
||||||
class PoolMonitor(neutron.NeutronAPIDictWrapper):
|
class PoolMonitor(neutron.NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron load balancer pool health monitor"""
|
"""Wrapper for neutron load balancer pool health monitor."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
super(PoolMonitor, self).__init__(apiresource)
|
super(PoolMonitor, self).__init__(apiresource)
|
||||||
|
|
|
@ -62,7 +62,7 @@ class NeutronAPIDictWrapper(base.APIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Agent(NeutronAPIDictWrapper):
|
class Agent(NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron agents"""
|
"""Wrapper for neutron agents."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['admin_state'] = \
|
apiresource['admin_state'] = \
|
||||||
|
@ -71,7 +71,7 @@ class Agent(NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Network(NeutronAPIDictWrapper):
|
class Network(NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron Networks"""
|
"""Wrapper for neutron Networks."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['admin_state'] = \
|
apiresource['admin_state'] = \
|
||||||
|
@ -84,7 +84,7 @@ class Network(NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Subnet(NeutronAPIDictWrapper):
|
class Subnet(NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron subnets"""
|
"""Wrapper for neutron subnets."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['ipver_str'] = get_ipver_str(apiresource['ip_version'])
|
apiresource['ipver_str'] = get_ipver_str(apiresource['ip_version'])
|
||||||
|
@ -92,7 +92,7 @@ class Subnet(NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Port(NeutronAPIDictWrapper):
|
class Port(NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron ports"""
|
"""Wrapper for neutron ports."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
apiresource['admin_state'] = \
|
apiresource['admin_state'] = \
|
||||||
|
@ -110,7 +110,7 @@ class Profile(NeutronAPIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
class Router(NeutronAPIDictWrapper):
|
class Router(NeutronAPIDictWrapper):
|
||||||
"""Wrapper for neutron routers"""
|
"""Wrapper for neutron routers."""
|
||||||
|
|
||||||
def __init__(self, apiresource):
|
def __init__(self, apiresource):
|
||||||
#apiresource['admin_state'] = \
|
#apiresource['admin_state'] = \
|
||||||
|
@ -412,7 +412,7 @@ class FloatingIpManager(network_base.FloatingIpManager):
|
||||||
|
|
||||||
|
|
||||||
def get_ipver_str(ip_version):
|
def get_ipver_str(ip_version):
|
||||||
"""Convert an ip version number to a human-friendly string"""
|
"""Convert an ip version number to a human-friendly string."""
|
||||||
return IP_VERSION_DICT.get(ip_version, '')
|
return IP_VERSION_DICT.get(ip_version, '')
|
||||||
|
|
||||||
|
|
||||||
|
@ -476,8 +476,7 @@ def network_get(request, network_id, expand_subnet=True, **params):
|
||||||
|
|
||||||
|
|
||||||
def network_create(request, **kwargs):
|
def network_create(request, **kwargs):
|
||||||
"""
|
"""Create a subnet on a specified network.
|
||||||
Create a subnet on a specified network.
|
|
||||||
:param request: request context
|
:param request: request context
|
||||||
:param tenant_id: (optional) tenant id of the network created
|
:param tenant_id: (optional) tenant id of the network created
|
||||||
:param name: (optional) name of the network created
|
:param name: (optional) name of the network created
|
||||||
|
@ -519,8 +518,7 @@ def subnet_get(request, subnet_id, **params):
|
||||||
|
|
||||||
|
|
||||||
def subnet_create(request, network_id, cidr, ip_version, **kwargs):
|
def subnet_create(request, network_id, cidr, ip_version, **kwargs):
|
||||||
"""
|
"""Create a subnet on a specified network.
|
||||||
Create a subnet on a specified network.
|
|
||||||
:param request: request context
|
:param request: request context
|
||||||
:param network_id: network id a subnet is created on
|
:param network_id: network id a subnet is created on
|
||||||
:param cidr: subnet IP address range
|
:param cidr: subnet IP address range
|
||||||
|
@ -567,8 +565,7 @@ def port_get(request, port_id, **params):
|
||||||
|
|
||||||
|
|
||||||
def port_create(request, network_id, **kwargs):
|
def port_create(request, network_id, **kwargs):
|
||||||
"""
|
"""Create a port on a specified network.
|
||||||
Create a port on a specified network.
|
|
||||||
:param request: request context
|
:param request: request context
|
||||||
:param network_id: network id a subnet is created on
|
:param network_id: network id a subnet is created on
|
||||||
:param device_id: (optional) device id attached to the port
|
:param device_id: (optional) device id attached to the port
|
||||||
|
|
|
@ -166,7 +166,7 @@ class SecurityGroup(base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupRule(base.APIResourceWrapper):
|
class SecurityGroupRule(base.APIResourceWrapper):
|
||||||
""" Wrapper for individual rules in a SecurityGroup. """
|
"""Wrapper for individual rules in a SecurityGroup."""
|
||||||
_attrs = ['id', 'ip_protocol', 'from_port', 'to_port', 'ip_range', 'group']
|
_attrs = ['id', 'ip_protocol', 'from_port', 'to_port', 'ip_range', 'group']
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
@ -686,8 +686,7 @@ def list_extensions(request):
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def extension_supported(extension_name, request):
|
def extension_supported(extension_name, request):
|
||||||
"""
|
"""this method will determine if nova supports a given extension name.
|
||||||
this method will determine if nova supports a given extension name.
|
|
||||||
example values for the extension_name include AdminActions, ConsoleOutput,
|
example values for the extension_name include AdminActions, ConsoleOutput,
|
||||||
etc.
|
etc.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -75,7 +75,7 @@ class PseudoFolder(base.APIDictWrapper):
|
||||||
|
|
||||||
|
|
||||||
def _objectify(items, container_name):
|
def _objectify(items, container_name):
|
||||||
""" Splits a listing of objects into their appropriate wrapper classes. """
|
"""Splits a listing of objects into their appropriate wrapper classes."""
|
||||||
objects = []
|
objects = []
|
||||||
|
|
||||||
# Deal with objects and object pseudo-folders first, save subdirs for later
|
# Deal with objects and object pseudo-folders first, save subdirs for later
|
||||||
|
|
|
@ -30,7 +30,7 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class TokenAuth(object):
|
class TokenAuth(object):
|
||||||
"""Simple Token Authentication handler for trove api"""
|
"""Simple Token Authentication handler for trove api."""
|
||||||
|
|
||||||
def __init__(self, client, auth_strategy, auth_url, username, password,
|
def __init__(self, client, auth_strategy, auth_url, username, password,
|
||||||
tenant, region, service_type, service_name, service_url):
|
tenant, region, service_type, service_name, service_url):
|
||||||
|
|
|
@ -25,7 +25,7 @@ from django.conf import settings # noqa
|
||||||
|
|
||||||
|
|
||||||
def openstack(request):
|
def openstack(request):
|
||||||
""" Context processor necessary for OpenStack Dashboard functionality.
|
"""Context processor necessary for OpenStack Dashboard functionality.
|
||||||
|
|
||||||
The following variables are added to the request context:
|
The following variables are added to the request context:
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ class DomainFilterAction(tables.FilterAction):
|
||||||
return multidomain_support
|
return multidomain_support
|
||||||
|
|
||||||
def filter(self, table, domains, filter_string):
|
def filter(self, table, domains, filter_string):
|
||||||
""" Naive case-insensitive search """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
|
|
||||||
def comp(domain):
|
def comp(domain):
|
||||||
|
|
|
@ -72,7 +72,7 @@ class ModifyAccess(tables.LinkAction):
|
||||||
|
|
||||||
class FlavorFilterAction(tables.FilterAction):
|
class FlavorFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, flavors, filter_string):
|
def filter(self, table, flavors, filter_string):
|
||||||
""" Really naive case-insensitive search. """
|
"""Really naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
|
|
||||||
def comp(flavor):
|
def comp(flavor):
|
||||||
|
|
|
@ -85,7 +85,7 @@ class ManageUsersLink(tables.LinkAction):
|
||||||
|
|
||||||
class GroupFilterAction(tables.FilterAction):
|
class GroupFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, groups, filter_string):
|
def filter(self, table, groups, filter_string):
|
||||||
""" Naive case-insensitive search """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
|
|
||||||
def comp(group):
|
def comp(group):
|
||||||
|
@ -112,7 +112,7 @@ class GroupsTable(tables.DataTable):
|
||||||
|
|
||||||
class UserFilterAction(tables.FilterAction):
|
class UserFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, users, filter_string):
|
def filter(self, table, users, filter_string):
|
||||||
""" Naive case-insensitive search """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [user for user in users
|
return [user for user in users
|
||||||
if q in user.name.lower()
|
if q in user.name.lower()
|
||||||
|
|
|
@ -68,5 +68,5 @@ class UpdateView(views.UpdateView):
|
||||||
|
|
||||||
|
|
||||||
class DetailView(views.DetailView):
|
class DetailView(views.DetailView):
|
||||||
""" Admin placeholder for image detail view. """
|
"""Admin placeholder for image detail view."""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -60,7 +60,7 @@ class AdminUpdateRow(project_tables.UpdateRow):
|
||||||
|
|
||||||
class AdminInstanceFilterAction(tables.FilterAction):
|
class AdminInstanceFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, instances, filter_string):
|
def filter(self, table, instances, filter_string):
|
||||||
""" Naive case-insensitive search. """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [instance for instance in instances
|
return [instance for instance in instances
|
||||||
if q in instance.name.lower()]
|
if q in instance.name.lower()]
|
||||||
|
|
|
@ -84,7 +84,7 @@ class GlobalDiskUsageTab(tabs.TableTab):
|
||||||
preload = False
|
preload = False
|
||||||
|
|
||||||
def get_global_disk_usage_data(self):
|
def get_global_disk_usage_data(self):
|
||||||
""" Disk usage table data aggregated by project """
|
"""Disk usage table data aggregated by project."""
|
||||||
request = self.tab_group.request
|
request = self.tab_group.request
|
||||||
return list_of_resource_aggregates(request,
|
return list_of_resource_aggregates(request,
|
||||||
ceilometer.GlobalDiskUsage.meters)
|
ceilometer.GlobalDiskUsage.meters)
|
||||||
|
@ -214,7 +214,7 @@ class GlobalStatsTab(tabs.Tab):
|
||||||
meter_titles[name] = hint
|
meter_titles[name] = hint
|
||||||
|
|
||||||
class MetersWrap(object):
|
class MetersWrap(object):
|
||||||
""" A quick wrapper for meter and associated titles. """
|
"""A quick wrapper for meter and associated titles."""
|
||||||
def __init__(self, meter, meter_titles):
|
def __init__(self, meter, meter_titles):
|
||||||
self.name = meter
|
self.name = meter
|
||||||
self.title = meter_titles.get(meter, "")
|
self.title = meter_titles.get(meter, "")
|
||||||
|
|
|
@ -174,7 +174,7 @@ class SamplesView(TemplateView):
|
||||||
query = []
|
query = []
|
||||||
|
|
||||||
def filter_by_meter_name(resource):
|
def filter_by_meter_name(resource):
|
||||||
""" Function for filtering of the list of resources.
|
"""Function for filtering of the list of resources.
|
||||||
|
|
||||||
Will pick the right resources according to currently selected
|
Will pick the right resources according to currently selected
|
||||||
meter.
|
meter.
|
||||||
|
|
|
@ -109,7 +109,7 @@ class DeleteTenantsAction(tables.DeleteAction):
|
||||||
|
|
||||||
class TenantFilterAction(tables.FilterAction):
|
class TenantFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, tenants, filter_string):
|
def filter(self, table, tenants, filter_string):
|
||||||
""" Really naive case-insensitive search. """
|
"""Really naive case-insensitive search."""
|
||||||
# FIXME(gabriel): This should be smarter. Written for demo purposes.
|
# FIXME(gabriel): This should be smarter. Written for demo purposes.
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ class DeleteRolesAction(tables.DeleteAction):
|
||||||
|
|
||||||
class RoleFilterAction(tables.FilterAction):
|
class RoleFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, roles, filter_string):
|
def filter(self, table, roles, filter_string):
|
||||||
""" Naive case-insensitive search """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [role for role in roles
|
return [role for role in roles
|
||||||
if q in role.name.lower()]
|
if q in role.name.lower()]
|
||||||
|
|
|
@ -115,7 +115,7 @@ class DeleteUsersAction(tables.DeleteAction):
|
||||||
|
|
||||||
class UserFilterAction(tables.FilterAction):
|
class UserFilterAction(tables.FilterAction):
|
||||||
def filter(self, table, users, filter_string):
|
def filter(self, table, users, filter_string):
|
||||||
""" Naive case-insensitive search """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [user for user in users
|
return [user for user in users
|
||||||
if q in user.name.lower()
|
if q in user.name.lower()
|
||||||
|
|
|
@ -36,7 +36,7 @@ class DeleteVolumeType(tables.DeleteAction):
|
||||||
class VolumesFilterAction(tables.FilterAction):
|
class VolumesFilterAction(tables.FilterAction):
|
||||||
|
|
||||||
def filter(self, table, volumes, filter_string):
|
def filter(self, table, volumes, filter_string):
|
||||||
""" Naive case-insensitive search. """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [volume for volume in volumes
|
return [volume for volume in volumes
|
||||||
if q in volume.display_name.lower()]
|
if q in volume.display_name.lower()]
|
||||||
|
|
|
@ -47,8 +47,7 @@ class DeleteContainer(tables.DeleteAction):
|
||||||
api.swift.swift_delete_container(request, obj_id)
|
api.swift.swift_delete_container(request, obj_id)
|
||||||
|
|
||||||
def get_success_url(self, request=None):
|
def get_success_url(self, request=None):
|
||||||
"""
|
"""Returns the URL to redirect to after a successful action.
|
||||||
Returns the URL to redirect to after a successful action.
|
|
||||||
"""
|
"""
|
||||||
current_container = self.table.kwargs.get("container_name", None)
|
current_container = self.table.kwargs.get("container_name", None)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ class ContainerView(browsers.ResourceBrowserView):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def objects(self):
|
def objects(self):
|
||||||
""" Returns a list of objects given the subfolder's path.
|
"""Returns a list of objects given the subfolder's path.
|
||||||
|
|
||||||
The path is from the kwargs of the request.
|
The path is from the kwargs of the request.
|
||||||
"""
|
"""
|
||||||
|
@ -93,13 +93,13 @@ class ContainerView(browsers.ResourceBrowserView):
|
||||||
return getattr(item, "content_type", None) == content_type
|
return getattr(item, "content_type", None) == content_type
|
||||||
|
|
||||||
def get_objects_data(self):
|
def get_objects_data(self):
|
||||||
""" Returns a list of objects within the current folder. """
|
"""Returns a list of objects within the current folder."""
|
||||||
filtered_objects = [item for item in self.objects
|
filtered_objects = [item for item in self.objects
|
||||||
if not self.is_subdir(item)]
|
if not self.is_subdir(item)]
|
||||||
return filtered_objects
|
return filtered_objects
|
||||||
|
|
||||||
def get_subfolders_data(self):
|
def get_subfolders_data(self):
|
||||||
""" Returns a list of subfolders within the current folder. """
|
"""Returns a list of subfolders within the current folder."""
|
||||||
filtered_objects = [item for item in self.objects
|
filtered_objects = [item for item in self.objects
|
||||||
if self.is_subdir(item)]
|
if self.is_subdir(item)]
|
||||||
return filtered_objects
|
return filtered_objects
|
||||||
|
|
|
@ -65,8 +65,7 @@ class SetInstanceDetails(workflows.Step):
|
||||||
|
|
||||||
|
|
||||||
class AddDatabasesAction(workflows.Action):
|
class AddDatabasesAction(workflows.Action):
|
||||||
"""
|
"""Initialize the database with users/databases. This tab will honor
|
||||||
Initialize the database with users/databases. This tab will honor
|
|
||||||
the settings which should be a list of permissions required:
|
the settings which should be a list of permissions required:
|
||||||
|
|
||||||
* TROVE_ADD_USER_PERMS = []
|
* TROVE_ADD_USER_PERMS = []
|
||||||
|
|
|
@ -44,8 +44,7 @@ IMAGES_INDEX_URL = reverse('horizon:project:images_and_snapshots:index')
|
||||||
|
|
||||||
class CreateImageFormTests(test.TestCase):
|
class CreateImageFormTests(test.TestCase):
|
||||||
def test_no_location_or_file(self):
|
def test_no_location_or_file(self):
|
||||||
"""
|
"""The form will not be valid if both copy_from and image_file are not
|
||||||
The form will not be valid if both copy_from and image_file are not
|
|
||||||
provided.
|
provided.
|
||||||
"""
|
"""
|
||||||
post = {
|
post = {
|
||||||
|
@ -62,8 +61,7 @@ class CreateImageFormTests(test.TestCase):
|
||||||
|
|
||||||
@override_settings(HORIZON_IMAGES_ALLOW_UPLOAD=False)
|
@override_settings(HORIZON_IMAGES_ALLOW_UPLOAD=False)
|
||||||
def test_image_upload_disabled(self):
|
def test_image_upload_disabled(self):
|
||||||
"""
|
"""If HORIZON_IMAGES_ALLOW_UPLOAD is false, the image_file field widget
|
||||||
If HORIZON_IMAGES_ALLOW_UPLOAD is false, the image_file field widget
|
|
||||||
will be a HiddenInput widget instead of a FileInput widget.
|
will be a HiddenInput widget instead of a FileInput widget.
|
||||||
"""
|
"""
|
||||||
form = forms.CreateImageForm({})
|
form = forms.CreateImageForm({})
|
||||||
|
|
|
@ -18,8 +18,7 @@ from openstack_dashboard.api import glance
|
||||||
|
|
||||||
|
|
||||||
def get_available_images(request, project_id=None, images_cache=None):
|
def get_available_images(request, project_id=None, images_cache=None):
|
||||||
"""
|
"""Returns a list of images that are public or owned by the given
|
||||||
Returns a list of images that are public or owned by the given
|
|
||||||
project_id. If project_id is not specified, only public images
|
project_id. If project_id is not specified, only public images
|
||||||
are returned.
|
are returned.
|
||||||
|
|
||||||
|
|
|
@ -548,7 +548,7 @@ TASK_DISPLAY_CHOICES = (
|
||||||
class InstancesFilterAction(tables.FilterAction):
|
class InstancesFilterAction(tables.FilterAction):
|
||||||
|
|
||||||
def filter(self, table, instances, filter_string):
|
def filter(self, table, instances, filter_string):
|
||||||
""" Naive case-insensitive search. """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [instance for instance in instances
|
return [instance for instance in instances
|
||||||
if q in instance.name.lower()]
|
if q in instance.name.lower()]
|
||||||
|
|
|
@ -217,7 +217,8 @@ class SetInstanceDetailsAction(workflows.Action):
|
||||||
"""By default, returns the available flavors, sorted by RAM
|
"""By default, returns the available flavors, sorted by RAM
|
||||||
usage (ascending).
|
usage (ascending).
|
||||||
Override these behaviours with a CREATE_INSTANCE_FLAVOR_SORT dict
|
Override these behaviours with a CREATE_INSTANCE_FLAVOR_SORT dict
|
||||||
in local_settings.py."""
|
in local_settings.py.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
flavors = api.nova.flavor_list(request)
|
flavors = api.nova.flavor_list(request)
|
||||||
flavor_sort = getattr(settings, 'CREATE_INSTANCE_FLAVOR_SORT', {})
|
flavor_sort = getattr(settings, 'CREATE_INSTANCE_FLAVOR_SORT', {})
|
||||||
|
|
|
@ -368,7 +368,7 @@ class CreateNetwork(workflows.Workflow):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _delete_network(self, request, network):
|
def _delete_network(self, request, network):
|
||||||
"""Delete the created network when subnet creation failed"""
|
"""Delete the created network when subnet creation failed."""
|
||||||
try:
|
try:
|
||||||
api.neutron.network_delete(request, network.id)
|
api.neutron.network_delete(request, network.id)
|
||||||
msg = _('Delete the created network "%s" '
|
msg = _('Delete the created network "%s" '
|
||||||
|
|
|
@ -31,8 +31,7 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RuleCIDRField(fields.IPField):
|
class RuleCIDRField(fields.IPField):
|
||||||
"""
|
"""Extends IPField to allow ('any','external') keywords and requires CIDR
|
||||||
Extends IPField to allow ('any','external') keywords and requires CIDR
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs['mask'] = True
|
kwargs['mask'] = True
|
||||||
|
|
|
@ -29,9 +29,7 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def exception_to_validation_msg(e):
|
def exception_to_validation_msg(e):
|
||||||
'''
|
"""Extracts a validation message to display to the user."""
|
||||||
Extracts a validation message to display to the user.
|
|
||||||
'''
|
|
||||||
try:
|
try:
|
||||||
error = json.loads(str(e))
|
error = json.loads(str(e))
|
||||||
# NOTE(jianingy): if no message exists, we just return 'None'
|
# NOTE(jianingy): if no message exists, we just return 'None'
|
||||||
|
|
|
@ -140,9 +140,7 @@ def get_resource_status(status):
|
||||||
|
|
||||||
|
|
||||||
def get_resource_image(status, type):
|
def get_resource_image(status, type):
|
||||||
'''
|
"""Sets the image url and in_progress action sw based on status."""
|
||||||
Sets the image url and in_progress action sw based on status.
|
|
||||||
'''
|
|
||||||
resource_type = get_resource_type(type)
|
resource_type = get_resource_type(type)
|
||||||
resource_status = get_resource_status(status)
|
resource_status = get_resource_status(status)
|
||||||
resource_state = resource_type + "_" + resource_status
|
resource_state = resource_type + "_" + resource_status
|
||||||
|
|
|
@ -132,8 +132,9 @@ def get_attachment_name(request, attachment):
|
||||||
|
|
||||||
|
|
||||||
class AttachmentColumn(tables.Column):
|
class AttachmentColumn(tables.Column):
|
||||||
"""
|
"""Customized column class.
|
||||||
Customized column class that does complex processing on the attachments
|
|
||||||
|
So it that does complex processing on the attachments
|
||||||
for a volume instance.
|
for a volume instance.
|
||||||
"""
|
"""
|
||||||
def get_raw_data(self, volume):
|
def get_raw_data(self, volume):
|
||||||
|
@ -184,7 +185,7 @@ class VolumesTableBase(tables.DataTable):
|
||||||
class VolumesFilterAction(tables.FilterAction):
|
class VolumesFilterAction(tables.FilterAction):
|
||||||
|
|
||||||
def filter(self, table, volumes, filter_string):
|
def filter(self, table, volumes, filter_string):
|
||||||
""" Naive case-insensitive search. """
|
"""Naive case-insensitive search."""
|
||||||
q = filter_string.lower()
|
q = filter_string.lower()
|
||||||
return [volume for volume in volumes
|
return [volume for volume in volumes
|
||||||
if q in volume.display_name.lower()]
|
if q in volume.display_name.lower()]
|
||||||
|
@ -230,8 +231,7 @@ class DetachVolume(tables.BatchAction):
|
||||||
|
|
||||||
|
|
||||||
class AttachedInstanceColumn(tables.Column):
|
class AttachedInstanceColumn(tables.Column):
|
||||||
"""
|
"""Customized column class that does complex processing on the attachments
|
||||||
Customized column class that does complex processing on the attachments
|
|
||||||
for a volume instance.
|
for a volume instance.
|
||||||
"""
|
"""
|
||||||
def get_raw_data(self, attachment):
|
def get_raw_data(self, attachment):
|
||||||
|
|
|
@ -46,7 +46,7 @@ def get_tenant_choices(request):
|
||||||
|
|
||||||
class CreateNetworkProfile(forms.SelfHandlingForm):
|
class CreateNetworkProfile(forms.SelfHandlingForm):
|
||||||
|
|
||||||
""" Create Network Profile form."""
|
"""Create Network Profile form."""
|
||||||
|
|
||||||
name = forms.CharField(max_length=255,
|
name = forms.CharField(max_length=255,
|
||||||
label=_("Name"),
|
label=_("Name"),
|
||||||
|
@ -115,7 +115,7 @@ class CreateNetworkProfile(forms.SelfHandlingForm):
|
||||||
|
|
||||||
class UpdateNetworkProfile(forms.SelfHandlingForm):
|
class UpdateNetworkProfile(forms.SelfHandlingForm):
|
||||||
|
|
||||||
""" Update Network Profile form."""
|
"""Update Network Profile form."""
|
||||||
|
|
||||||
profile_id = forms.CharField(label=_("ID"),
|
profile_id = forms.CharField(label=_("ID"),
|
||||||
widget=forms.HiddenInput())
|
widget=forms.HiddenInput())
|
||||||
|
|
|
@ -60,7 +60,8 @@ def reset():
|
||||||
|
|
||||||
|
|
||||||
def check(actions, request, target={}):
|
def check(actions, request, target={}):
|
||||||
"""
|
"""Check user permission.
|
||||||
|
|
||||||
Check if the user has permission to the action according
|
Check if the user has permission to the action according
|
||||||
to policy setting.
|
to policy setting.
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
class APIResource(api_base.APIResourceWrapper):
|
class APIResource(api_base.APIResourceWrapper):
|
||||||
""" Simple APIResource for testing """
|
"""Simple APIResource for testing."""
|
||||||
_attrs = ['foo', 'bar', 'baz']
|
_attrs = ['foo', 'bar', 'baz']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -44,7 +44,7 @@ class APIResource(api_base.APIResourceWrapper):
|
||||||
|
|
||||||
|
|
||||||
class APIDict(api_base.APIDictWrapper):
|
class APIDict(api_base.APIDictWrapper):
|
||||||
""" Simple APIDict for testing """
|
"""Simple APIDict for testing."""
|
||||||
_attrs = ['foo', 'bar', 'baz']
|
_attrs = ['foo', 'bar', 'baz']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -116,7 +116,7 @@ class APIDictWrapperTests(test.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class ApiHelperTests(test.TestCase):
|
class ApiHelperTests(test.TestCase):
|
||||||
""" Tests for functions that don't use one of the api objects """
|
"""Tests for functions that don't use one of the api objects."""
|
||||||
|
|
||||||
def test_url_for(self):
|
def test_url_for(self):
|
||||||
url = api_base.url_for(self.request, 'image')
|
url = api_base.url_for(self.request, 'image')
|
||||||
|
|
|
@ -50,8 +50,7 @@ class RoleAPITests(test.APITestCase):
|
||||||
self.roles = self.roles.list()
|
self.roles = self.roles.list()
|
||||||
|
|
||||||
def test_remove_tenant_user(self):
|
def test_remove_tenant_user(self):
|
||||||
"""
|
"""Tests api.keystone.remove_tenant_user
|
||||||
Tests api.keystone.remove_tenant_user
|
|
||||||
|
|
||||||
Verifies that remove_tenant_user is called with the right arguments
|
Verifies that remove_tenant_user is called with the right arguments
|
||||||
after iterating the user's roles.
|
after iterating the user's roles.
|
||||||
|
|
|
@ -108,8 +108,7 @@ class RequestFactoryWithMessages(RequestFactory):
|
||||||
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
||||||
"The SKIP_UNITTESTS env variable is set.")
|
"The SKIP_UNITTESTS env variable is set.")
|
||||||
class TestCase(horizon_helpers.TestCase):
|
class TestCase(horizon_helpers.TestCase):
|
||||||
"""
|
"""Specialized base test case class for Horizon which gives access to
|
||||||
Specialized base test case class for Horizon which gives access to
|
|
||||||
numerous additional features:
|
numerous additional features:
|
||||||
|
|
||||||
* A full suite of test data through various attached objects and
|
* A full suite of test data through various attached objects and
|
||||||
|
@ -180,8 +179,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||||
utils.get_user = get_user
|
utils.get_user = get_user
|
||||||
|
|
||||||
def assertRedirectsNoFollow(self, response, expected_url):
|
def assertRedirectsNoFollow(self, response, expected_url):
|
||||||
"""
|
"""Asserts that the given response issued a 302 redirect without
|
||||||
Asserts that the given response issued a 302 redirect without
|
|
||||||
processing the view which is redirected to.
|
processing the view which is redirected to.
|
||||||
"""
|
"""
|
||||||
assert (response.status_code / 100 == 3), \
|
assert (response.status_code / 100 == 3), \
|
||||||
|
@ -191,8 +189,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
def assertNoFormErrors(self, response, context_name="form"):
|
def assertNoFormErrors(self, response, context_name="form"):
|
||||||
"""
|
"""Asserts that the response either does not contain a form in its
|
||||||
Asserts that the response either does not contain a form in its
|
|
||||||
context, or that if it does, that form has no errors.
|
context, or that if it does, that form has no errors.
|
||||||
"""
|
"""
|
||||||
context = getattr(response, "context", {})
|
context = getattr(response, "context", {})
|
||||||
|
@ -204,8 +201,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||||
|
|
||||||
def assertFormErrors(self, response, count=0, message=None,
|
def assertFormErrors(self, response, count=0, message=None,
|
||||||
context_name="form"):
|
context_name="form"):
|
||||||
"""
|
"""Asserts that the response does contain a form in its
|
||||||
Asserts that the response does contain a form in its
|
|
||||||
context, and that form has errors, if count were given,
|
context, and that form has errors, if count were given,
|
||||||
it must match the exact numbers of errors
|
it must match the exact numbers of errors
|
||||||
"""
|
"""
|
||||||
|
@ -226,8 +222,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class BaseAdminViewTests(TestCase):
|
class BaseAdminViewTests(TestCase):
|
||||||
"""
|
"""A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||||
A ``TestCase`` subclass which sets an active user with the "admin" role
|
|
||||||
for testing admin-only views and functionality.
|
for testing admin-only views and functionality.
|
||||||
"""
|
"""
|
||||||
def setActiveUser(self, *args, **kwargs):
|
def setActiveUser(self, *args, **kwargs):
|
||||||
|
@ -248,8 +243,7 @@ class BaseAdminViewTests(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class APITestCase(TestCase):
|
class APITestCase(TestCase):
|
||||||
"""
|
"""The ``APITestCase`` class is for use with tests which deal with the
|
||||||
The ``APITestCase`` class is for use with tests which deal with the
|
|
||||||
underlying clients rather than stubbing out the
|
underlying clients rather than stubbing out the
|
||||||
openstack_dashboard.api.* methods.
|
openstack_dashboard.api.* methods.
|
||||||
"""
|
"""
|
||||||
|
@ -258,8 +252,7 @@ class APITestCase(TestCase):
|
||||||
utils.patch_middleware_get_user()
|
utils.patch_middleware_get_user()
|
||||||
|
|
||||||
def fake_keystoneclient(request, admin=False):
|
def fake_keystoneclient(request, admin=False):
|
||||||
"""
|
"""Wrapper function which returns the stub keystoneclient. Only
|
||||||
Wrapper function which returns the stub keystoneclient. Only
|
|
||||||
necessary because the function takes too many arguments to
|
necessary because the function takes too many arguments to
|
||||||
conveniently be a lambda.
|
conveniently be a lambda.
|
||||||
"""
|
"""
|
||||||
|
@ -414,8 +407,7 @@ class SeleniumTestCase(horizon_helpers.SeleniumTestCase):
|
||||||
|
|
||||||
|
|
||||||
class SeleniumAdminTestCase(SeleniumTestCase):
|
class SeleniumAdminTestCase(SeleniumTestCase):
|
||||||
"""
|
"""A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||||
A ``TestCase`` subclass which sets an active user with the "admin" role
|
|
||||||
for testing admin-only views and functionality.
|
for testing admin-only views and functionality.
|
||||||
"""
|
"""
|
||||||
def setActiveUser(self, *args, **kwargs):
|
def setActiveUser(self, *args, **kwargs):
|
||||||
|
|
|
@ -53,8 +53,7 @@ def load_test_data(load_onto=None):
|
||||||
|
|
||||||
|
|
||||||
class TestData(object):
|
class TestData(object):
|
||||||
"""
|
"""Holder object for test data. Any functions passed to the init method
|
||||||
Holder object for test data. Any functions passed to the init method
|
|
||||||
will be called with the ``TestData`` object as their only argument. They
|
will be called with the ``TestData`` object as their only argument. They
|
||||||
can then load data onto the object as desired.
|
can then load data onto the object as desired.
|
||||||
|
|
||||||
|
@ -79,7 +78,7 @@ class TestData(object):
|
||||||
|
|
||||||
|
|
||||||
class TestDataContainer(object):
|
class TestDataContainer(object):
|
||||||
""" A container for test data objects.
|
"""A container for test data objects.
|
||||||
|
|
||||||
The behavior of this class is meant to mimic a "manager" class, which
|
The behavior of this class is meant to mimic a "manager" class, which
|
||||||
has convenient shortcuts for common actions like "list", "filter", "get",
|
has convenient shortcuts for common actions like "list", "filter", "get",
|
||||||
|
@ -89,7 +88,7 @@ class TestDataContainer(object):
|
||||||
self._objects = []
|
self._objects = []
|
||||||
|
|
||||||
def add(self, *args):
|
def add(self, *args):
|
||||||
""" Add a new object to this container.
|
"""Add a new object to this container.
|
||||||
|
|
||||||
Generally this method should only be used during data loading, since
|
Generally this method should only be used during data loading, since
|
||||||
adding data during a test can affect the results of other tests.
|
adding data during a test can affect the results of other tests.
|
||||||
|
@ -99,12 +98,11 @@ class TestDataContainer(object):
|
||||||
self._objects.append(obj)
|
self._objects.append(obj)
|
||||||
|
|
||||||
def list(self):
|
def list(self):
|
||||||
""" Returns a list of all objects in this container. """
|
"""Returns a list of all objects in this container."""
|
||||||
return self._objects
|
return self._objects
|
||||||
|
|
||||||
def filter(self, filtered=None, **kwargs):
|
def filter(self, filtered=None, **kwargs):
|
||||||
"""
|
"""Returns objects in this container whose attributes match the given
|
||||||
Returns objects in this container whose attributes match the given
|
|
||||||
keyword arguments.
|
keyword arguments.
|
||||||
"""
|
"""
|
||||||
if filtered is None:
|
if filtered is None:
|
||||||
|
@ -121,8 +119,7 @@ class TestDataContainer(object):
|
||||||
return self.filter(filtered=filter(get_match, filtered), **kwargs)
|
return self.filter(filtered=filter(get_match, filtered), **kwargs)
|
||||||
|
|
||||||
def get(self, **kwargs):
|
def get(self, **kwargs):
|
||||||
"""
|
"""Returns the single object in this container whose attributes match
|
||||||
Returns the single object in this container whose attributes match
|
|
||||||
the given keyword arguments. An error will be raised if the arguments
|
the given keyword arguments. An error will be raised if the arguments
|
||||||
provided don't return exactly one match.
|
provided don't return exactly one match.
|
||||||
"""
|
"""
|
||||||
|
@ -135,7 +132,7 @@ class TestDataContainer(object):
|
||||||
return matches.pop()
|
return matches.pop()
|
||||||
|
|
||||||
def first(self):
|
def first(self):
|
||||||
""" Returns the first object from this container. """
|
"""Returns the first object from this container."""
|
||||||
return self._objects[0]
|
return self._objects[0]
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
|
|
|
@ -23,7 +23,7 @@ from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
|
|
||||||
class ErrorPageTests(test.TestCase):
|
class ErrorPageTests(test.TestCase):
|
||||||
""" Tests for error pages """
|
"""Tests for error pages."""
|
||||||
urls = 'openstack_dashboard.test.error_pages_urls'
|
urls = 'openstack_dashboard.test.error_pages_urls'
|
||||||
|
|
||||||
def test_500_error(self):
|
def test_500_error(self):
|
||||||
|
|
|
@ -25,7 +25,7 @@ class FakeUser(object):
|
||||||
|
|
||||||
|
|
||||||
class TemplateRenderTest(test.TestCase):
|
class TemplateRenderTest(test.TestCase):
|
||||||
""" Tests for templates render """
|
"""Tests for templates render."""
|
||||||
|
|
||||||
def test_openrc_html_escape(self):
|
def test_openrc_html_escape(self):
|
||||||
context = {
|
context = {
|
||||||
|
|
|
@ -275,8 +275,7 @@ class ProjectUsage(BaseUsage):
|
||||||
|
|
||||||
class CsvDataMixin(object):
|
class CsvDataMixin(object):
|
||||||
|
|
||||||
"""
|
"""CSV data Mixin - provides handling for CSV data.
|
||||||
CSV data Mixin - provides handling for CSV data
|
|
||||||
|
|
||||||
.. attribute:: columns
|
.. attribute:: columns
|
||||||
|
|
||||||
|
@ -318,10 +317,7 @@ class CsvDataMixin(object):
|
||||||
|
|
||||||
class BaseCsvResponse(CsvDataMixin, HttpResponse):
|
class BaseCsvResponse(CsvDataMixin, HttpResponse):
|
||||||
|
|
||||||
"""
|
"""Base CSV response class. Provides handling of CSV data."""
|
||||||
Base CSV response class. Provides handling of CSV data.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, request, template, context, content_type, **kwargs):
|
def __init__(self, request, template, context, content_type, **kwargs):
|
||||||
super(BaseCsvResponse, self).__init__()
|
super(BaseCsvResponse, self).__init__()
|
||||||
|
@ -358,8 +354,7 @@ if VERSION >= (1, 5, 0):
|
||||||
|
|
||||||
class BaseCsvStreamingResponse(CsvDataMixin, StreamingHttpResponse):
|
class BaseCsvStreamingResponse(CsvDataMixin, StreamingHttpResponse):
|
||||||
|
|
||||||
"""
|
"""Base CSV Streaming class. Provides streaming response for CSV data.
|
||||||
Base CSV Streaming class. Provides streaming response for CSV data.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, request, template, context, content_type, **kwargs):
|
def __init__(self, request, template, context, content_type, **kwargs):
|
||||||
|
|
|
@ -60,7 +60,7 @@ QUOTA_FIELDS = NOVA_QUOTA_FIELDS + CINDER_QUOTA_FIELDS + NEUTRON_QUOTA_FIELDS
|
||||||
|
|
||||||
|
|
||||||
class QuotaUsage(dict):
|
class QuotaUsage(dict):
|
||||||
""" Tracks quota limit, used, and available for a given set of quotas."""
|
"""Tracks quota limit, used, and available for a given set of quotas."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.usages = defaultdict(dict)
|
self.usages = defaultdict(dict)
|
||||||
|
@ -77,7 +77,7 @@ class QuotaUsage(dict):
|
||||||
return repr(dict(self.usages))
|
return repr(dict(self.usages))
|
||||||
|
|
||||||
def add_quota(self, quota):
|
def add_quota(self, quota):
|
||||||
""" Adds an internal tracking reference for the given quota. """
|
"""Adds an internal tracking reference for the given quota."""
|
||||||
if quota.limit is None or quota.limit == -1:
|
if quota.limit is None or quota.limit == -1:
|
||||||
# Handle "unlimited" quotas.
|
# Handle "unlimited" quotas.
|
||||||
self.usages[quota.name]['quota'] = float("inf")
|
self.usages[quota.name]['quota'] = float("inf")
|
||||||
|
@ -86,7 +86,7 @@ class QuotaUsage(dict):
|
||||||
self.usages[quota.name]['quota'] = int(quota.limit)
|
self.usages[quota.name]['quota'] = int(quota.limit)
|
||||||
|
|
||||||
def tally(self, name, value):
|
def tally(self, name, value):
|
||||||
""" Adds to the "used" metric for the given quota. """
|
"""Adds to the "used" metric for the given quota."""
|
||||||
value = value or 0 # Protection against None.
|
value = value or 0 # Protection against None.
|
||||||
# Start at 0 if this is the first value.
|
# Start at 0 if this is the first value.
|
||||||
if 'used' not in self.usages[name]:
|
if 'used' not in self.usages[name]:
|
||||||
|
@ -96,7 +96,7 @@ class QuotaUsage(dict):
|
||||||
self.update_available(name)
|
self.update_available(name)
|
||||||
|
|
||||||
def update_available(self, name):
|
def update_available(self, name):
|
||||||
""" Updates the "available" metric for the given quota. """
|
"""Updates the "available" metric for the given quota."""
|
||||||
available = self.usages[name]['quota'] - self.usages[name]['used']
|
available = self.usages[name]['quota'] - self.usages[name]['used']
|
||||||
if available < 0:
|
if available < 0:
|
||||||
available = 0
|
available = 0
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -34,8 +34,7 @@ builtins = _
|
||||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py
|
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py
|
||||||
# E127 continuation line over-indented for visual indent
|
# E127 continuation line over-indented for visual indent
|
||||||
# E128 continuation line under-indented for visual indent
|
# E128 continuation line under-indented for visual indent
|
||||||
# H4xx docstrings
|
|
||||||
# H701 empty localization string
|
# H701 empty localization string
|
||||||
# H702 Formatting operation should be outside of localization method call
|
# H702 Formatting operation should be outside of localization method call
|
||||||
# H803 git commit title should not end with period (disabled on purpose, see bug #1236621)
|
# H803 git commit title should not end with period (disabled on purpose, see bug #1236621)
|
||||||
ignore = E127,E128,H4,H701,H702,H803
|
ignore = E127,E128,H701,H702,H803
|
||||||
|
|
Loading…
Reference in New Issue