Plan editing form

Put back the form for selecting the numbers of instances. It will be
later replaced with angular machinery.

A the moment there is no way to update plan parameters on submit.

Change-Id: I5bd345f38b1f630c384858f405d767e0003d3354
This commit is contained in:
Radomir Dopieralski 2014-08-13 12:41:19 +02:00
parent 3d814c9b97
commit 8cdb60cb94
8 changed files with 138 additions and 57 deletions

View File

@ -12,12 +12,42 @@
# License for the specific language governing permissions and limitations
# under the License.
import django.forms
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.forms
import horizon.messages
from tuskar_ui import api
import tuskar_ui.forms
def _get_role_count(plan, role):
return plan.parameter_value(role.node_count_parameter_name, 0)
class EditPlan(horizon.forms.SelfHandlingForm):
def __init__(self, *args, **kwargs):
super(EditPlan, self).__init__(*args, **kwargs)
self.plan = api.tuskar.OvercloudPlan.get_the_plan(self.request)
self.fields.update(self._role_count_fields(self.plan))
def _role_count_fields(self, plan):
fields = {}
for role in plan.role_list:
field = django.forms.IntegerField(
label=role.name,
widget=tuskar_ui.forms.NumberPickerInput,
initial=_get_role_count(plan, role),
# XXX Dirty hack for requiring a controller node.
required=(role.name == 'Controller'),
)
fields['%s-count' % role.id] = field
return fields
def handle(self, request, data):
# XXX Update the plan.
return True
class DeployOvercloud(horizon.forms.SelfHandlingForm):

View File

@ -64,28 +64,22 @@
{% endfor %}
</div>
{% endif %}
<br />
<div class="actions page-actions pull-left">
<hr>
<a href="{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}"
class="btn btn-danger ajax-modal">
<i class="icon-fire icon-white"></i>
<i class="glyphicon glyphicon-fire"></i>
{% trans "Undeploy" %}
</a>
</div>
{% else %}
Ready to get deployed
<ul>
{% for role in roles %}
{% if role.planned_node_count > 0 %}
<li>{{ role.planned_node_count }} x {{ role.name }} </li>
{% endif %}
{% endfor %}
</ul>
<div class="actions page-actions pull-left">
<a href="{% url 'horizon:infrastructure:overview:deploy_confirmation' %}"
class="btn btn-danger ajax-modal">
<i class="icon-fire icon-white"></i>
{% trans "Deploy" %}
</a>
</div>
<h4>
<span class="text-success" style="font-size: x-large; vertical-align:middle">
<i class="glyphicon glyphicon-ok-circle"></i>
</span>
{% trans "Ready to get deployed" %}
</h4>
<a href="{% url 'horizon:infrastructure:overview:deploy_confirmation' %}"
class="btn btn-danger ajax-modal">
<i class="glyphicon glyphicon-cloud-upload"></i>
{% trans "Deploy" %}
</a>
{% endif %}

View File

@ -1,25 +1,32 @@
{% load i18n %}
{% load url from future %}
{% load form_helpers %}
<h4>{% trans "Deployment Roles" %}</h4>
<form method="POST" action="." class="deployment-roles-form">
{% csrf_token %}
{% include 'horizon/common/_form_errors.html' with form=form %}
Deployment Roles
<table class="table">
{% for role in roles %}
<tbody>
<tr>
<td>
{% if stack %}
{% if role.total_node_count != role.deployed_node_count %}
{{ role.deployed_node_count }} / {{ role.total_node_count }}
{% else %}
{{ role.total_node_count }}
{% endif %}
{% else %}
{{ role.planned_node_count }}
{% endif %}
</td>
<td>{{ role.name }}</td>
</tr>
</tbody>
{% endfor %}
</table>
{% for field in form.visible_fields %}
<div class="form-group well well-sm clearfix{% if field.errors %} error{% endif %} {{ field.css_classes }}">
<div class="col-sm-2">
{{ field|add_bootstrap_class }}
</div>
<div class="col-sm-10">
<label class="control-label" for="{{ field.id_for_label }}">
{{ field.label }}
</label>
{% for error in field.errors %}
<span class="help-block"><span class="text-danger">
{{ error }}
</span></span>
{% empty %}
{% if field.help_text %}
<span class="help-block">{{ field.help_text }}</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
<hr>
<button type="submit" class="btn btn-default">{% trans "Update" %}</button>
</form>

View File

@ -78,7 +78,11 @@ class OverviewTests(test.BaseAdminViewTests):
call_args_list[0][0][0]
self.assertListEqual(
api.tuskar.OvercloudPlan.get_the_plan.call_args_list,
[call(request), call(request)])
[
call(request),
call(request),
call(request),
])
self.assertTemplateUsed(
res, 'infrastructure/overview/index.html')
self.assertTemplateUsed(
@ -96,8 +100,13 @@ class OverviewTests(test.BaseAdminViewTests):
) as (OvercloudPlan, stack_get_mock, stack_events_mock):
res = self.client.get(INDEX_URL)
request = OvercloudPlan.get_the_plan.call_args_list[0][0][0]
self.assertListEqual(OvercloudPlan.get_the_plan.call_args_list,
[call(request), call(request)])
self.assertListEqual(
OvercloudPlan.get_the_plan.call_args_list,
[
call(request),
call(request),
call(request),
])
self.assertTemplateUsed(
res, 'infrastructure/overview/index.html')

View File

@ -12,12 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
import django.views.generic
import heatclient
import horizon.forms
from horizon.utils import memoized
from horizon import views as horizon_views
from tuskar_ui import api
from tuskar_ui.infrastructure.overview import forms
@ -82,8 +81,18 @@ class StackMixin(object):
return stack
class IndexView(horizon_views.APIView, StackMixin):
class IndexView(django.views.generic.FormView, StackMixin):
template_name = 'infrastructure/overview/index.html'
form_class = forms.EditPlan
success_url = reverse_lazy(INDEX_URL)
def get_form(self, form_class):
return form_class(self.request, **self.get_form_kwargs())
def get_context_data(self, *args, **kwargs):
context = super(IndexView, self).get_context_data(*args, **kwargs)
context.update(self.get_data(self.request, context))
return context
def get_data(self, request, context, *args, **kwargs):
plan = api.tuskar.OvercloudPlan.get_the_plan(request)

View File

@ -1,5 +1,17 @@
<div class="number_picker unselectable">
<a ng-click="decrementValue()" class="btn btn-link" ng-class="{disabled: disabledInput || disableArrow()}"><i ng-hide="disabledInput" class="icon-chevron-left"></i></a>
<a
ng-click="decrementValue()"
class="arrow-left"
ng-class="{disabled: disabledInput || disableArrow()}"
>
<i ng-hide="disabledInput" class="glyphicon glyphicon-chevron-left"></i>
</a>
<input type="text" value="{$value$}" ng-model="value">
<a ng-click="incrementValue()" class="btn btn-link" ng-class="{disabled: disabledInput}"><i ng-hide="disabledInput" class="icon-chevron-right"></i></a>
<a
ng-click="incrementValue()"
class="arrow-right"
ng-class="{disabled: disabledInput}"
>
<i ng-hide="disabledInput" class="glyphicon glyphicon-chevron-right"></i>
</a>
</div>

View File

@ -1,16 +1,34 @@
// Number picker widget
div.number_picker {
margin: 5px 0;
margin: 5px 14px;
width: 35px;
height: 35px;
padding: 0;
display: inline-block;
position: relative;
vertical-align: middle;
input {
width: 35px;
height: 30px;
margin-bottom: 0;
text-align: center;
border: 0;
width: 20px;
display: block;
margin: 8px auto;
padding: 0;
text-align: center;
}
a {
display: block;
width: 14px;
position: absolute;
top: 8px;
cursor: default;
&.disabled {
cursor: default;
}
&.arrow-left {
left: -14px;
}
&.arrow-right {
right: -14px;
}
}
}

View File

@ -1,8 +1,10 @@
{% load form_helpers %}
<div class="control-group{% if field.errors %} error{% endif %} {{ field.css_classes }}">
<label class="control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="controls">
<div class="form-group{% if field.errors %} error{% endif %} {{ field.css_classes }}">
<label class="control-label col-sm-3" for="{{ field.id_for_label }}">
{{ field.label }}
</label>
<div class="col-sm-9">
{{ field|add_bootstrap_class }}
{% for error in field.errors %}
<span class="help-inline text-error">{{ error }}</span>