diff --git a/muranodashboard/catalog/urls.py b/muranodashboard/catalog/urls.py index c13eb58f6..1023f9f7b 100644 --- a/muranodashboard/catalog/urls.py +++ b/muranodashboard/catalog/urls.py @@ -15,13 +15,11 @@ from django.conf.urls import patterns, url from muranodashboard.catalog import views from muranodashboard.catalog import image - -from muranodashboard.environments import views as env_views from muranodashboard.dynamic_ui import services VIEW_MOD = 'muranodashboard.catalog.views' -wizard_view = env_views.Wizard.as_view( +wizard_view = views.Wizard.as_view( services.get_app_forms, condition_dict=services.condition_getter) urlpatterns = patterns( diff --git a/muranodashboard/catalog/views.py b/muranodashboard/catalog/views.py index d2dafa222..3da2ed888 100644 --- a/muranodashboard/catalog/views.py +++ b/muranodashboard/catalog/views.py @@ -11,20 +11,35 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + +import copy +import functools +import json import logging -from django.views.generic import list -from horizon import tabs -from django import shortcuts -from django.conf import settings -from django.contrib.auth import REDIRECT_FIELD_NAME -from django.contrib.auth.decorators import login_required -from django.utils.http import is_safe_url -from muranodashboard.catalog import tabs as catalog_tabs -from muranodashboard.environments import api -from muranodashboard.environments import views -from muranodashboard.dynamic_ui import services import re +from django.conf import settings +from django.contrib import auth +from django.contrib.auth import decorators as auth_dec +from django.contrib.formtools.wizard import views as wizard_views +from django.core import urlresolvers as url +from django import http +from django import shortcuts +from django.utils import decorators as django_dec +from django.utils import http as http_utils +from django.utils.translation import ugettext_lazy as _ # noqa +from django.views.generic import list as list_view +from horizon import messages +from horizon import tabs +from horizon.forms import views +from horizon import exceptions +from muranoclient.common import exceptions as exc +from muranodashboard.catalog import tabs as catalog_tabs +from muranodashboard.dynamic_ui import services +from muranodashboard.dynamic_ui import helpers +from muranodashboard.environments import api +from muranodashboard.environments import consts +from muranodashboard import utils LOG = logging.getLogger(__name__) @@ -64,10 +79,11 @@ def get_environments_context(request): return context -@login_required -def switch(request, environment_id, redirect_field_name=REDIRECT_FIELD_NAME): +@auth_dec.login_required +def switch(request, environment_id, + redirect_field_name=auth.REDIRECT_FIELD_NAME): redirect_to = request.REQUEST.get(redirect_field_name, '') - if not is_safe_url(url=redirect_to, host=request.get_host()): + if not http_utils.is_safe_url(url=redirect_to, host=request.get_host()): redirect_to = settings.LOGIN_REDIRECT_URL for env in get_available_environments(request): @@ -95,12 +111,12 @@ def create_quick_environment(request): return api.environment_create(request, params) -@login_required +@auth_dec.login_required def quick_deploy(request, app_id): env = create_quick_environment(request) try: - view = views.Wizard.as_view(services.get_app_forms, - condition_dict=services.condition_getter) + view = Wizard.as_view(services.get_app_forms, + condition_dict=services.condition_getter) return view(request, app_id=app_id, environment_id=env.id, do_redirect=True, drop_wm_form=True) except: @@ -108,7 +124,155 @@ def quick_deploy(request, app_id): raise -class IndexView(list.ListView): +class LazyWizard(wizard_views.SessionWizardView): + """The class which defers evaluation of form_list and condition_dict + until view method is called. So, each time we load a page with a dynamic + UI form, it will have markup/logic from the newest YAML-file definition. + """ + @django_dec.classonlymethod + def as_view(cls, initforms, *initargs, **initkwargs): + """ + Main entry point for a request-response process. + """ + # sanitize keyword arguments + for key in initkwargs: + if key in cls.http_method_names: + raise TypeError(u"You tried to pass in the %s method name as a" + u" keyword argument to %s(). Don't do that." + % (key, cls.__name__)) + if not hasattr(cls, key): + raise TypeError(u"%s() received an invalid keyword %r" % ( + cls.__name__, key)) + + def view(request, *args, **kwargs): + forms = initforms + if hasattr(initforms, '__call__'): + forms = initforms(request, kwargs) + _kwargs = copy.copy(initkwargs) + + _kwargs = cls.get_initkwargs(forms, *initargs, **_kwargs) + + cdict = _kwargs.get('condition_dict') + if cdict and hasattr(cdict, '__call__'): + _kwargs['condition_dict'] = cdict(request, kwargs) + + self = cls(**_kwargs) + if hasattr(self, 'get') and not hasattr(self, 'head'): + self.head = self.get + self.request = request + self.args = args + self.kwargs = kwargs + return self.dispatch(request, *args, **kwargs) + + # take name and docstring from class + functools.update_wrapper(view, cls, updated=()) + + # and possible attributes set by decorators + # like csrf_exempt from dispatch + functools.update_wrapper(view, cls.dispatch, assigned=()) + return view + + +class Wizard(views.ModalFormMixin, LazyWizard): + template_name = 'services/wizard_create.html' + do_redirect = False + + def get_prefix(self, *args, **kwargs): + base = super(Wizard, self).get_prefix(*args, **kwargs) + fmt = utils.BlankFormatter() + return fmt.format('{0}_{environment_id}_{app_id}', base, **kwargs) + + def done(self, form_list, **kwargs): + environment_id = kwargs.get('environment_id') + env_url = url.reverse('horizon:murano:environments:services', + args=(environment_id,)) + app_name = services.get_service_name(self.request, + kwargs.get('app_id')) + + service = form_list[0].service + attributes = service.extract_attributes() + attributes = helpers.insert_hidden_ids(attributes) + + storage = attributes.setdefault('?', {}).setdefault( + consts.DASHBOARD_ATTRS_KEY, {}) + storage['name'] = app_name + + wm_form_data = service.cleaned_data.get('workflowManagement') + if wm_form_data: + do_redirect = not wm_form_data['StayAtCatalog'] + else: + do_redirect = self.get_wizard_flag('do_redirect') + + fail_url = url.reverse("horizon:murano:environments:index") + try: + srv = api.service_create(self.request, environment_id, attributes) + except exc.HTTPForbidden: + msg = _("Sorry, you can't add application right now. " + "The environment is deploying.") + exceptions.handle(self.request, msg, redirect=fail_url) + except Exception: + exceptions.handle(self.request, + _("Sorry, you can't add application right now."), + redirect=fail_url) + else: + message = "The '{0}' application successfully " \ + "added to environment.".format(app_name) + + messages.success(self.request, message) + + if do_redirect: + return http.HttpResponseRedirect(env_url) + else: + srv_id = getattr(srv, '?')['id'] + return self.create_hacked_response(srv_id, attributes['name']) + + def create_hacked_response(self, obj_id, obj_name): + # copy-paste from horizon.forms.views.ModalFormView; should be done + # that way until we move here from django Wizard to horizon workflow + if views.ADD_TO_FIELD_HEADER in self.request.META: + field_id = self.request.META[views.ADD_TO_FIELD_HEADER] + response = http.HttpResponse(json.dumps([obj_id, obj_name])) + response["X-Horizon-Add-To-Field"] = field_id + return response + else: + return http.HttpResponse('Done') + + def get_form_initial(self, step): + init_dict = {'request': self.request, + 'environment_id': self.kwargs.get('environment_id')} + + return self.initial_dict.get(step, init_dict) + + def _get_wizard_param(self, key): + param = self.kwargs.get(key) + return param if param is not None else self.request.POST.get(key) + + def get_wizard_flag(self, key): + value = self._get_wizard_param(key) + if isinstance(value, basestring): + return value.lower() == 'true' + else: + return value + + def get_context_data(self, form, **kwargs): + context = super(Wizard, self).get_context_data(form=form, **kwargs) + app_id = self.kwargs.get('app_id') + app = api.muranoclient(self.request).packages.get(app_id) + + context['field_descriptions'] = services.get_app_field_descriptions( + self.request, app_id, self.steps.index) + context.update({'type': app.fully_qualified_name, + 'service_name': app.name, + 'app_id': app_id, + 'environment_id': self.kwargs.get('environment_id'), + 'do_redirect': self.get_wizard_flag('do_redirect'), + 'drop_wm_form': self.get_wizard_flag('drop_wm_form'), + 'prefix': self.prefix, + }) + return context + + +class IndexView(list_view.ListView): paginate_by = 6 def get_queryset(self): diff --git a/muranodashboard/dynamic_ui/services.py b/muranodashboard/dynamic_ui/services.py index 99e674d66..4d75390b0 100644 --- a/muranodashboard/dynamic_ui/services.py +++ b/muranodashboard/dynamic_ui/services.py @@ -207,7 +207,7 @@ def get_service_name(request, app_id): return app.name -def get_service_field_descriptions(request, app_id, index): +def get_app_field_descriptions(request, app_id, index): app = import_app(request, app_id) form_cls = app.forms[index] diff --git a/muranodashboard/environments/views.py b/muranodashboard/environments/views.py index 7387f4145..7b44fb316 100644 --- a/muranodashboard/environments/views.py +++ b/muranodashboard/environments/views.py @@ -12,213 +12,50 @@ # License for the specific language governing permissions and limitations # under the License. -import json import logging -import copy -from functools import update_wrapper -from django.core.urlresolvers import reverse, reverse_lazy -from django.utils.translation import ugettext_lazy as _ -from django.contrib.formtools.wizard.views import SessionWizardView -from django.http import HttpResponseRedirect # noqa -from django.http import HttpResponse # noqa +from django.core import urlresolvers as url +from django.utils.translation import ugettext_lazy as _ # noqa +from django.http import HttpResponse from django.views import generic -from django.utils.decorators import classonlymethod from horizon import exceptions from horizon import tabs from horizon import tables from horizon import workflows -from horizon import messages -from horizon.forms import views as hz_views -from tables import EnvironmentsTable -from tables import DeploymentsTable -from tables import EnvConfigTable -from workflows import CreateEnvironment, UpdateEnvironment -from tabs import ServicesTabs, DeploymentTabs, EnvironmentDetailsTabs -from . import api -from muranoclient.common.exceptions import HTTPUnauthorized, \ - CommunicationError, HTTPInternalServerError, HTTPForbidden, HTTPNotFound -from muranodashboard.dynamic_ui.services import get_service_name -from muranodashboard.dynamic_ui.services import get_service_field_descriptions -from muranodashboard.dynamic_ui import helpers -from muranodashboard.environments import consts -from muranodashboard import utils +from muranoclient.common import exceptions as exc +from muranodashboard.environments import api +from muranodashboard.environments import tabs as env_tabs +from muranodashboard.environments import tables as env_tables +from muranodashboard.environments import workflows as env_workflows LOG = logging.getLogger(__name__) -class LazyWizard(SessionWizardView): - """The class which defers evaluation of form_list and condition_dict - until view method is called. So, each time we load a page with a dynamic - UI form, it will have markup/logic from the newest YAML-file definition. - """ - @classonlymethod - def as_view(cls, initforms, *initargs, **initkwargs): - """ - Main entry point for a request-response process. - """ - # sanitize keyword arguments - for key in initkwargs: - if key in cls.http_method_names: - raise TypeError(u"You tried to pass in the %s method name as a" - u" keyword argument to %s(). Don't do that." - % (key, cls.__name__)) - if not hasattr(cls, key): - raise TypeError(u"%s() received an invalid keyword %r" % ( - cls.__name__, key)) - - def view(request, *args, **kwargs): - forms = initforms - if hasattr(initforms, '__call__'): - forms = initforms(request, kwargs) - _kwargs = copy.copy(initkwargs) - - _kwargs = cls.get_initkwargs(forms, *initargs, **_kwargs) - - cdict = _kwargs.get('condition_dict') - if cdict and hasattr(cdict, '__call__'): - _kwargs['condition_dict'] = cdict(request, kwargs) - - self = cls(**_kwargs) - if hasattr(self, 'get') and not hasattr(self, 'head'): - self.head = self.get - self.request = request - self.args = args - self.kwargs = kwargs - return self.dispatch(request, *args, **kwargs) - - # take name and docstring from class - update_wrapper(view, cls, updated=()) - - # and possible attributes set by decorators - # like csrf_exempt from dispatch - update_wrapper(view, cls.dispatch, assigned=()) - return view - - -class Wizard(hz_views.ModalFormMixin, LazyWizard): - template_name = 'services/wizard_create.html' - do_redirect = False - - def get_prefix(self, *args, **kwargs): - base = super(Wizard, self).get_prefix(*args, **kwargs) - fmt = utils.BlankFormatter() - return fmt.format('{0}_{environment_id}_{app_id}', base, **kwargs) - - def done(self, form_list, **kwargs): - environment_id = kwargs.get('environment_id') - url = reverse('horizon:murano:environments:services', - args=(environment_id,)) - app_name = get_service_name(self.request, kwargs.get('app_id')) - - service = form_list[0].service - attributes = service.extract_attributes() - attributes = helpers.insert_hidden_ids(attributes) - - storage = attributes.setdefault('?', {}).setdefault( - consts.DASHBOARD_ATTRS_KEY, {}) - storage['name'] = app_name - - wm_form_data = service.cleaned_data.get('workflowManagement') - if wm_form_data: - do_redirect = not wm_form_data['StayAtCatalog'] - else: - do_redirect = self.get_wizard_flag('do_redirect') - - try: - srv = api.service_create(self.request, environment_id, attributes) - except HTTPForbidden: - msg = _("Sorry, you can\'t add application right now." - "The environment is deploying.") - redirect = reverse("horizon:murano:environments:index") - exceptions.handle(self.request, msg, redirect=redirect) - except Exception: - redirect = reverse("horizon:murano:environments:index") - exceptions.handle(self.request, - _("Sorry, you can't add application right now."), - redirect=redirect) - else: - message = "The '{0}' application successfully " \ - "added to environment.".format(app_name) - - messages.success(self.request, message) - - if do_redirect: - return HttpResponseRedirect(url) - else: - srv_id = getattr(srv, '?')['id'] - return self.create_hacked_response(srv_id, attributes['name']) - - def create_hacked_response(self, obj_id, obj_name): - # copy-paste from horizon.forms.views.ModalFormView; should be done - # that way until we move here from django Wizard to horizon workflow - if hz_views.ADD_TO_FIELD_HEADER in self.request.META: - field_id = self.request.META[hz_views.ADD_TO_FIELD_HEADER] - response = HttpResponse(json.dumps([obj_id, obj_name])) - response["X-Horizon-Add-To-Field"] = field_id - return response - else: - return HttpResponse('Done') - - def get_form_initial(self, step): - init_dict = {'request': self.request, - 'environment_id': self.kwargs.get('environment_id')} - - return self.initial_dict.get(step, init_dict) - - def _get_wizard_param(self, key): - param = self.kwargs.get(key) - return param if param is not None else self.request.POST.get(key) - - def get_wizard_flag(self, key): - value = self._get_wizard_param(key) - if isinstance(value, basestring): - return value.lower() == 'true' - else: - return value - - def get_context_data(self, form, **kwargs): - context = super(Wizard, self).get_context_data(form=form, **kwargs) - app_id = self.kwargs.get('app_id') - app = api.muranoclient(self.request).packages.get(app_id) - context['field_descriptions'] = get_service_field_descriptions( - self.request, app_id, self.steps.index) - context.update({'type': app.fully_qualified_name, - 'service_name': app.name, - 'app_id': app_id, - 'environment_id': self.kwargs.get('environment_id'), - 'do_redirect': self.get_wizard_flag('do_redirect'), - 'drop_wm_form': self.get_wizard_flag('drop_wm_form'), - 'prefix': self.prefix, - }) - return context - - class IndexView(tables.DataTableView): - table_class = EnvironmentsTable + table_class = env_tables.EnvironmentsTable template_name = 'environments/index.html' def get_data(self): environments = [] try: environments = api.environments_list(self.request) - except CommunicationError: + except exc.CommunicationError: exceptions.handle(self.request, 'Could not connect to Murano API \ Service, check connection details') - except HTTPInternalServerError: + except exc.HTTPInternalServerError: exceptions.handle(self.request, 'Murano API Service is not responding. \ Try again later') - except HTTPUnauthorized: + except exc.HTTPUnauthorized: exceptions.handle(self.request, ignore=True, escalate=True) return environments class EnvironmentDetails(tabs.TabbedTableView): - tab_group_class = EnvironmentDetailsTabs + tab_group_class = env_tabs.EnvironmentDetailsTabs template_name = 'services/index.html' def get_context_data(self, **kwargs): @@ -231,13 +68,13 @@ class EnvironmentDetails(tabs.TabbedTableView): except: msg = _("Sorry, this environment doesn't exist anymore") - redirect = reverse("horizon:murano:environments:index") + redirect = url.reverse("horizon:murano:environments:index") exceptions.handle(self.request, msg, redirect=redirect) return context class DetailServiceView(tabs.TabView): - tab_group_class = ServicesTabs + tab_group_class = env_tabs.ServicesTabs template_name = 'services/details.html' def get_context_data(self, **kwargs): @@ -255,11 +92,11 @@ class DetailServiceView(tabs.TabView): self.service = api.service_get(self.request, self.environment_id, service_id) - except HTTPUnauthorized: + except exc.HTTPUnauthorized: exceptions.handle(self.request) - except HTTPForbidden: - redirect = reverse('horizon:murano:environments:index') + except exc.HTTPForbidden: + redirect = url.reverse('horizon:murano:environments:index') exceptions.handle(self.request, _('Unable to retrieve details for ' 'service'), @@ -274,7 +111,7 @@ class DetailServiceView(tabs.TabView): class CreateEnvironmentView(workflows.WorkflowView): - workflow_class = CreateEnvironment + workflow_class = env_workflows.CreateEnvironment template_name = 'environments/create.html' def get_initial(self): @@ -285,9 +122,9 @@ class CreateEnvironmentView(workflows.WorkflowView): class EditEnvironmentView(workflows.WorkflowView): - workflow_class = UpdateEnvironment + workflow_class = env_workflows.UpdateEnvironment template_name = 'environments/update.html' - success_url = reverse_lazy("horizon:murano:environments:index") + success_url = url.reverse_lazy("horizon:murano:environments:index") def get_context_data(self, **kwargs): context = super(EditEnvironmentView, self).get_context_data(**kwargs) @@ -301,7 +138,7 @@ class EditEnvironmentView(workflows.WorkflowView): self._object = \ api.environment_get(self.request, environment_id) except: - redirect = reverse("horizon:murano:environments:index") + redirect = url.reverse("horizon:murano:environments:index") msg = _('Unable to retrieve environment details.') exceptions.handle(self.request, msg, redirect=redirect) return self._object @@ -314,7 +151,7 @@ class EditEnvironmentView(workflows.WorkflowView): class DeploymentsView(tables.DataTableView): - table_class = DeploymentsTable + table_class = env_tables.DeploymentsTable template_name = 'deployments/index.html' def get_context_data(self, **kwargs): @@ -325,7 +162,7 @@ class DeploymentsView(tables.DataTableView): context['environment_name'] = env.name except: msg = _("Sorry, this environment doesn't exist anymore") - redirect = reverse("horizon:murano:environments:index") + redirect = url.reverse("horizon:murano:environments:index") exceptions.handle(self.request, msg, redirect=redirect) return context @@ -337,20 +174,20 @@ class DeploymentsView(tables.DataTableView): deployments = api.deployments_list(self.request, self.environment_id) - except HTTPForbidden: + except exc.HTTPForbidden: msg = _('Unable to retrieve list of deployments') - exceptions.handle(self.request, msg, redirect=reverse(ns_url)) + exceptions.handle(self.request, msg, redirect=url.reverse(ns_url)) - except HTTPInternalServerError: + except exc.HTTPInternalServerError: msg = _("Environment with id %s doesn't exist anymore" % self.environment_id) - exceptions.handle(self.request, msg, redirect=reverse(ns_url)) + exceptions.handle(self.request, msg, redirect=url.reverse(ns_url)) return deployments class DeploymentDetailsView(tabs.TabbedTableView): - tab_group_class = DeploymentTabs - table_class = EnvConfigTable + tab_group_class = env_tabs.DeploymentTabs + table_class = env_tables.EnvConfigTable template_name = 'deployments/reports.html' def get_context_data(self, **kwargs): @@ -370,10 +207,10 @@ class DeploymentDetailsView(tabs.TabbedTableView): deployment = api.get_deployment_descr(self.request, self.environment_id, self.deployment_id) - except (HTTPInternalServerError, HTTPNotFound): + except (exc.HTTPInternalServerError, exc.HTTPNotFound): msg = _("Deployment with id %s doesn't exist anymore" % self.deployment_id) - redirect = reverse("horizon:murano:environments:deployments") + redirect = url.reverse("horizon:murano:environments:deployments") exceptions.handle(self.request, msg, redirect=redirect) return deployment @@ -383,10 +220,10 @@ class DeploymentDetailsView(tabs.TabbedTableView): logs = api.deployment_reports(self.request, self.environment_id, self.deployment_id) - except (HTTPInternalServerError, HTTPNotFound): + except (exc.HTTPInternalServerError, exc.HTTPNotFound): msg = _('Deployment with id %s doesn\'t exist anymore' % self.deployment_id) - redirect = reverse("horizon:murano:environments:deployments") + redirect = url.reverse("horizon:murano:environments:deployments") exceptions.handle(self.request, msg, redirect=redirect) return logs