Add "Preview Stack" action to Stacks table
This patch set adds "Preview Stack" button to Stacks table to provide user with a possibility to preview stack without creating it, as it is already implemented in CLI. Partially implements blueprint: heat-ui-improvement Change-Id: Idf92deb57f8213a403f102db467828087d91e79a
This commit is contained in:
parent
d844b0f13b
commit
cab3912b69
@ -102,6 +102,10 @@ def stack_create(request, password=None, **kwargs):
|
||||
return heatclient(request, password).stacks.create(**kwargs)
|
||||
|
||||
|
||||
def stack_preview(request, password=None, **kwargs):
|
||||
return heatclient(request, password).stacks.preview(**kwargs)
|
||||
|
||||
|
||||
def stack_update(request, stack_id, password=None, **kwargs):
|
||||
return heatclient(request, password).stacks.update(stack_id, **kwargs)
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
"cloudformation:ListStacks": "rule:deny_stack_user",
|
||||
"cloudformation:CreateStack": "rule:deny_stack_user",
|
||||
"cloudformation:PreviewStack": "rule:deny_stack_user",
|
||||
"cloudformation:DescribeStacks": "rule:deny_stack_user",
|
||||
"cloudformation:DeleteStack": "rule:deny_stack_user",
|
||||
"cloudformation:UpdateStack": "rule:deny_stack_user",
|
||||
|
@ -235,6 +235,12 @@ class ChangeTemplateForm(TemplateForm):
|
||||
'readonly'}))
|
||||
|
||||
|
||||
class PreviewTemplateForm(TemplateForm):
|
||||
class Meta(object):
|
||||
name = _('Preview Template')
|
||||
help_text = _('Select a new template to preview a stack.')
|
||||
|
||||
|
||||
class CreateStackForm(forms.SelfHandlingForm):
|
||||
|
||||
param_prefix = '__param_'
|
||||
@ -429,3 +435,40 @@ class EditStackForm(CreateStackForm):
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
|
||||
|
||||
class PreviewStackForm(CreateStackForm):
|
||||
|
||||
class Meta(object):
|
||||
name = _('Preview Stack Parameters')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.next_view = kwargs.pop('next_view')
|
||||
super(CreateStackForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def handle(self, request, data):
|
||||
prefix_length = len(self.param_prefix)
|
||||
params_list = [(k[prefix_length:], v) for (k, v) in six.iteritems(data)
|
||||
if k.startswith(self.param_prefix)]
|
||||
fields = {
|
||||
'stack_name': data.get('stack_name'),
|
||||
'timeout_mins': data.get('timeout_mins'),
|
||||
'disable_rollback': not(data.get('enable_rollback')),
|
||||
'parameters': dict(params_list),
|
||||
}
|
||||
|
||||
if data.get('template_data'):
|
||||
fields['template'] = data.get('template_data')
|
||||
else:
|
||||
fields['template_url'] = data.get('template_url')
|
||||
|
||||
if data.get('environment_data'):
|
||||
fields['environment'] = data.get('environment_data')
|
||||
|
||||
try:
|
||||
stack_preview = api.heat.stack_preview(self.request, **fields)
|
||||
request.method = 'GET'
|
||||
return self.next_view.as_view()(request,
|
||||
stack_preview=stack_preview)
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
|
@ -36,6 +36,14 @@ class LaunchStack(tables.LinkAction):
|
||||
policy_rules = (("orchestration", "cloudformation:CreateStack"),)
|
||||
|
||||
|
||||
class PreviewStack(tables.LinkAction):
|
||||
name = "preview"
|
||||
verbose_name = _("Preview Stack")
|
||||
url = "horizon:project:stacks:preview_template"
|
||||
classes = ("ajax-modal",)
|
||||
policy_rules = (("orchestration", "cloudformation:PreviewStack"),)
|
||||
|
||||
|
||||
class CheckStack(tables.BatchAction):
|
||||
name = "check"
|
||||
verbose_name = _("Check Stack")
|
||||
@ -277,6 +285,7 @@ class StacksTable(tables.DataTable):
|
||||
status_columns = ["status", ]
|
||||
row_class = StacksUpdateRow
|
||||
table_actions = (LaunchStack,
|
||||
PreviewStack,
|
||||
CheckStack,
|
||||
SuspendStack,
|
||||
ResumeStack,
|
||||
|
@ -0,0 +1,6 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Preview a new stack with the provided values." %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,59 @@
|
||||
{% extends "horizon/common/_modal.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block modal-header %}{% trans "Stack Preview" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="row-fluid stack-preview detail">
|
||||
<form>
|
||||
<dl class="dl-horizontal">
|
||||
{% for key, value in stack_preview.items %}
|
||||
{% if key != 'parameters' and key != 'resources' and key != 'links' %}
|
||||
<dt>{{ key }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</dl>
|
||||
|
||||
{% if stack_preview.parameters %}
|
||||
<dt>{% trans "Parameters" %}</dt>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
{% for key, value in stack_preview.parameters.items %}
|
||||
<dt>{{ key }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% endif %}
|
||||
|
||||
{% if stack_preview.links %}
|
||||
<dt>{% trans "Links" %}</dt>
|
||||
<hr class="header_rule">
|
||||
{% for link in stack_preview.links %}
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{{ link.rel }}</dt>
|
||||
<dd>{{ link.href }}</dd>
|
||||
</dl>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if stack_preview.resources %}
|
||||
<dt>{% trans "Resources" %}</dt>
|
||||
{% for resource in stack_preview.resources %}
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
{% for key, value in resource.items %}
|
||||
<dt>{{ key }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{% url 'horizon:project:access_and_security:index' %}" class="btn btn-default secondary cancel close">{% trans "Close" %}</a>
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Use one of the available template source options to specify the template to be used in previewing this stack." %}</p>
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Preview Stack" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/stacks/_preview.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Preview Stack Details" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/stacks/_preview_details.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Preview Template" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/stacks/_preview_template.html' %}
|
||||
{% endblock %}
|
@ -740,6 +740,54 @@ class StackTests(test.TestCase):
|
||||
def test_resume_stack(self):
|
||||
self._test_stack_action('resume')
|
||||
|
||||
@test.create_stubs({api.heat: ('stack_preview', 'template_validate')})
|
||||
def test_preview_stack(self):
|
||||
template = self.stack_templates.first()
|
||||
stack = self.stacks.first()
|
||||
|
||||
api.heat.template_validate(IsA(http.HttpRequest),
|
||||
template=template.data) \
|
||||
.AndReturn(json.loads(template.validate))
|
||||
|
||||
api.heat.stack_preview(IsA(http.HttpRequest),
|
||||
stack_name=stack.stack_name,
|
||||
timeout_mins=60,
|
||||
disable_rollback=True,
|
||||
template=template.data,
|
||||
parameters=IsA(dict)).AndReturn(stack)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:stacks:preview_template')
|
||||
res = self.client.get(url)
|
||||
self.assertTemplateUsed(res, 'project/stacks/preview_template.html')
|
||||
|
||||
form_data = {'template_source': 'raw',
|
||||
'template_data': template.data,
|
||||
'method': forms.PreviewTemplateForm.__name__}
|
||||
res = self.client.post(url, form_data)
|
||||
self.assertTemplateUsed(res, 'project/stacks/preview.html')
|
||||
|
||||
url = reverse('horizon:project:stacks:preview')
|
||||
form_data = {'template_source': 'raw',
|
||||
'template_data': template.data,
|
||||
'parameters': template.validate,
|
||||
'stack_name': stack.stack_name,
|
||||
"timeout_mins": 60,
|
||||
"disable_rollback": True,
|
||||
"__param_DBUsername": "admin",
|
||||
"__param_LinuxDistribution": "F17",
|
||||
"__param_InstanceType": "m1.small",
|
||||
"__param_KeyName": "test",
|
||||
"__param_DBPassword": "admin",
|
||||
"__param_DBRootPassword": "admin",
|
||||
"__param_DBName": "wordpress",
|
||||
'method': forms.PreviewStackForm.__name__}
|
||||
res = self.client.post(url, form_data)
|
||||
self.assertTemplateUsed(res, 'project/stacks/preview_details.html')
|
||||
self.assertEqual(res.context['stack_preview']['stack_name'],
|
||||
stack.stack_name)
|
||||
|
||||
|
||||
class TemplateFormTests(test.TestCase):
|
||||
|
||||
|
@ -22,6 +22,11 @@ urlpatterns = patterns(
|
||||
views.SelectTemplateView.as_view(),
|
||||
name='select_template'),
|
||||
url(r'^launch$', views.CreateStackView.as_view(), name='launch'),
|
||||
url(r'^preview_template$',
|
||||
views.PreviewTemplateView.as_view(), name='preview_template'),
|
||||
url(r'^preview$', views.PreviewStackView.as_view(), name='preview'),
|
||||
url(r'^preview_details$',
|
||||
views.PreviewStackDetailsView.as_view(), name='preview_details'),
|
||||
url(r'^stack/(?P<stack_id>[^/]+)/$',
|
||||
views.DetailView.as_view(), name='detail'),
|
||||
url(r'^(?P<stack_id>[^/]+)/change_template$',
|
||||
|
@ -27,6 +27,7 @@ from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
from horizon import views
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.project.stacks \
|
||||
import api as project_api
|
||||
@ -130,6 +131,22 @@ class ChangeTemplateView(forms.ModalFormView):
|
||||
return kwargs
|
||||
|
||||
|
||||
class PreviewTemplateView(forms.ModalFormView):
|
||||
template_name = 'project/stacks/preview_template.html'
|
||||
modal_header = _("Preview Template")
|
||||
form_id = "preview_template"
|
||||
form_class = project_forms.PreviewTemplateForm
|
||||
submit_label = _("Next")
|
||||
submit_url = reverse_lazy('horizon:project:stacks:preview_template')
|
||||
success_url = reverse_lazy('horizon:project:stacks:preview')
|
||||
page_title = _("Preview Template")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(PreviewTemplateView, self).get_form_kwargs()
|
||||
kwargs['next_view'] = PreviewStackView
|
||||
return kwargs
|
||||
|
||||
|
||||
class CreateStackView(forms.ModalFormView):
|
||||
form_class = project_forms.CreateStackForm
|
||||
template_name = 'project/stacks/create.html'
|
||||
@ -198,6 +215,33 @@ class EditStackView(CreateStackView):
|
||||
return self._stack
|
||||
|
||||
|
||||
class PreviewStackView(CreateStackView):
|
||||
template_name = 'project/stacks/preview.html'
|
||||
modal_header = _("Preview Stack")
|
||||
form_id = "preview_stack"
|
||||
form_class = project_forms.PreviewStackForm
|
||||
submit_label = _("Preview")
|
||||
submit_url = reverse_lazy('horizon:project:stacks:preview')
|
||||
success_url = reverse_lazy('horizon:project:stacks:index')
|
||||
page_title = _("Preview Stack")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(CreateStackView, self).get_form_kwargs()
|
||||
kwargs['next_view'] = PreviewStackDetailsView
|
||||
return kwargs
|
||||
|
||||
|
||||
class PreviewStackDetailsView(forms.ModalFormMixin, views.HorizonTemplateView):
|
||||
template_name = 'project/stacks/preview_details.html'
|
||||
page_title = _("Preview Stack Details")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(
|
||||
PreviewStackDetailsView, self).get_context_data(**kwargs)
|
||||
context['stack_preview'] = self.kwargs['stack_preview'].to_dict()
|
||||
return context
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = project_tabs.StackDetailTabs
|
||||
template_name = 'project/stacks/detail.html'
|
||||
|
Loading…
x
Reference in New Issue
Block a user