murano-dashboard/muranodashboard/catalog/views.py

346 lines
12 KiB
Python

# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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
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__)
ALL_CATEGORY_NAME = 'All'
class DictToObj(object):
def __init__(self, **kwargs):
for key, value in kwargs.iteritems():
setattr(self, key, value)
def get_available_environments(request):
envs = []
for env in api.environments_list(request):
obj = DictToObj(id=env.id, name=env.name, status=env.status)
envs.append(obj)
return envs
def is_valid_environment(environment, valid_environments):
for env in valid_environments:
if environment.id == env.id:
return True
return False
def get_environments_context(request):
envs = get_available_environments(request)
context = {'available_environments': envs}
environment = request.session.get('environment')
if environment and is_valid_environment(environment, envs):
context['environment'] = environment
elif envs:
context['environment'] = envs[0]
return context
@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 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):
if env.id == environment_id:
request.session['environment'] = env
break
return shortcuts.redirect(redirect_to)
def create_quick_environment(request):
quick_env_prefix = 'quick-env-'
quick_env_re = re.compile('^' + quick_env_prefix + '([\d]+)$')
def parse_number(env):
match = re.match(quick_env_re, env.name)
return int(match.group(1)) if match else 0
numbers = [parse_number(e) for e in api.environments_list(request)]
new_env_number = 1
if numbers:
numbers.sort()
new_env_number = numbers[-1] + 1
params = {'name': quick_env_prefix + str(new_env_number)}
return api.environment_create(request, params)
@auth_dec.login_required
def quick_deploy(request, app_id):
env = create_quick_environment(request)
try:
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:
api.environment_delete(request, env.id)
raise
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):
category = self.kwargs.get('category', ALL_CATEGORY_NAME)
query_params = {'type': 'Application'}
search = self.request.GET.get('search')
if search:
query_params['search'] = search
else:
if category != ALL_CATEGORY_NAME:
query_params['category'] = category
pkgs = []
with api.handled_exceptions(self.request):
client = api.muranoclient(self.request)
pkgs = client.packages.filter(**query_params)
return pkgs
def get_template_names(self):
return ['catalog/index.html']
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['latest_list'] = []
categories = []
with api.handled_exceptions(self.request):
client = api.muranoclient(self.request)
categories = client.packages.categories()
if not ALL_CATEGORY_NAME in categories:
categories.insert(0, ALL_CATEGORY_NAME)
current_category = self.kwargs.get('category', categories[0])
context['categories'] = categories
context['current_category'] = current_category
search = self.request.GET.get('search')
if search:
context['search'] = search
context.update(get_environments_context(self.request))
return context
class AppDetailsView(tabs.TabView):
tab_group_class = catalog_tabs.ApplicationTabs
template_name = 'catalog/app_details.html'
app = None
def get_data(self, **kwargs):
LOG.debug(('AppDetailsView get_data: {0}'.format(kwargs)))
app_id = kwargs.get('application_id')
self.app = api.muranoclient(self.request).packages.get(app_id)
return self.app
def get_context_data(self, **kwargs):
context = super(AppDetailsView, self).get_context_data(**kwargs)
LOG.debug('AppDetailsView get_context called with kwargs: {0}'.
format(kwargs))
context['app'] = self.app
context.update(get_environments_context(self.request))
return context
def get_tabs(self, request, *args, **kwargs):
app = self.get_data(**kwargs)
return self.tab_group_class(request, application=app, **kwargs)