Refresh progress page with AJAX
Instead of reloading the progress page constantly, update it with JavaScript. Only refresh when the deployment finished. Change-Id: I2c08c6f213dad59f410f6e5612fd1c2ed89f7eec
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load url from future%}
|
{% load url from future%}
|
||||||
|
{% load horizon %}
|
||||||
|
|
||||||
{% block deployment-icon %}
|
{% block deployment-icon %}
|
||||||
<i class="fa fa-spinner fa-spin text-info"></i>
|
<i class="fa fa-spinner fa-spin text-info"></i>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
><span class="sr-only">{{ progress }}% {% trans "Complete" %}</span></div>
|
><span class="sr-only">{{ progress }}% {% trans "Complete" %}</span></div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="deploy-last-events'>
|
||||||
{% if last_failed_events %}
|
{% if last_failed_events %}
|
||||||
<strong>{% trans "Last failed events:" %}</strong>
|
<strong>{% trans "Last failed events:" %}</strong>
|
||||||
{% for event in last_failed_events %}
|
{% for event in last_failed_events %}
|
||||||
@@ -46,6 +48,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p><a href="{% url 'horizon:infrastructure:history:index' %}">See full log</a></p>
|
<p><a href="{% url 'horizon:infrastructure:history:index' %}">See full log</a></p>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block deployment-buttons %}
|
{% block deployment-buttons %}
|
||||||
@@ -59,3 +62,22 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block templates %}
|
||||||
|
<script type="text/html" id="events-template">{% spaceless %}{% jstemplate %}
|
||||||
|
<strong>{% trans "Last failed events:" %}</strong>
|
||||||
|
[[#last_failed_events]]
|
||||||
|
<div>
|
||||||
|
<dl>
|
||||||
|
<dt>{% trans "Timestamp" %}</dt>
|
||||||
|
<dd><time datetime="[[ event_time ]]">[[ event_time ]]</time></dd>
|
||||||
|
<dt>{% trans "Resource Name" %}</dt>
|
||||||
|
<dd>[[ resource_name ]]</dd>
|
||||||
|
<dt>{% trans "Status" %}</dt>
|
||||||
|
<dd>[[ resource_status ]]</dd>
|
||||||
|
<dt>{% trans "Reason" %}</dt>
|
||||||
|
<dd>[[ resource_status_reason ]]</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
[[/last_failed_events]]
|
||||||
|
{% endjstemplate %}{% endspaceless %}</script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -4,13 +4,6 @@
|
|||||||
|
|
||||||
{% block title %}{% trans 'My OpenStack Deployment' %}{% endblock %}
|
{% block title %}{% trans 'My OpenStack Deployment' %}{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
|
||||||
{% if stack.is_deploying %}
|
|
||||||
<meta http-equiv="refresh" content="30">
|
|
||||||
{% endif %}
|
|
||||||
{{ block.super }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include 'horizon/common/_domain_page_header.html' with title=_('My OpenStack Deployment') %}
|
{% include 'horizon/common/_domain_page_header.html' with title=_('My OpenStack Deployment') %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load url from future %}
|
{% load url from future %}
|
||||||
|
{% load horizon %}
|
||||||
|
|
||||||
<h4>{% trans "Deployment Roles" %}</h4>
|
<h4>{% trans "Deployment Roles" %}</h4>
|
||||||
|
<div class="deploy-role-status">
|
||||||
{% for role in roles %}
|
{% for role in roles %}
|
||||||
<div class="alert well-sm clearfix
|
<div class="alert well-sm clearfix alert-{{ role.status }}">
|
||||||
{% if role.error_node_count %}
|
|
||||||
alert-danger
|
|
||||||
{% elif role.deployed_node_count == role.planned_node_count %}
|
|
||||||
alert-success
|
|
||||||
{% else %}
|
|
||||||
alert-info
|
|
||||||
{% endif %}
|
|
||||||
">
|
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
{% if role.deployed_node_count < role.planned_node_count %}
|
{% if role.finished %}
|
||||||
<strong>{{ role.deployed_node_count }}</strong><small class="text-muted">/{{ role.planned_node_count }}</small>
|
<strong>{{ role.deployed_node_count }}</strong><small class="text-muted">/{{ role.planned_node_count }}</small>
|
||||||
{% else %}
|
{% else %}
|
||||||
<strong>{{ role.planned_node_count }}</strong>
|
<strong>{{ role.planned_node_count }}</strong>
|
||||||
@@ -26,3 +20,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/html" id="roles-template">{% spaceless %}{% jstemplate %}
|
||||||
|
[[#roles]]
|
||||||
|
<div class="alert well-sm clearfix alert-[[status]]">
|
||||||
|
<div class="col-sm-2">
|
||||||
|
[[#finished]]
|
||||||
|
<strong>[[ planned_node_count ]]</strong>
|
||||||
|
[[/finished]]
|
||||||
|
[[^finished]]
|
||||||
|
<strong>[[ deployed_node_count ]]
|
||||||
|
</strong><small class="text-muted">/[[ planned_node_count ]]</small>
|
||||||
|
[[/finished]]
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-10"><strong>[[ name ]]</strong></div>
|
||||||
|
</div>
|
||||||
|
[[/roles]]
|
||||||
|
{% endjstemplate %}{% endspaceless %}</script>
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def _get_role_data(plan, stack, form, role):
|
|||||||
'role': role,
|
'role': role,
|
||||||
'name': role.name,
|
'name': role.name,
|
||||||
'planned_node_count': plan.get_role_node_count(role),
|
'planned_node_count': plan.get_role_node_count(role),
|
||||||
'field': form['%s-count' % role.id],
|
'field': form['%s-count' % role.id] if form else '',
|
||||||
}
|
}
|
||||||
|
|
||||||
if stack:
|
if stack:
|
||||||
@@ -55,6 +55,7 @@ def _get_role_data(plan, stack, form, role):
|
|||||||
error_node_count = 0
|
error_node_count = 0
|
||||||
waiting_node_count = node_count
|
waiting_node_count = node_count
|
||||||
|
|
||||||
|
status = 'warning'
|
||||||
if nodes:
|
if nodes:
|
||||||
deployed_node_count = sum(1 for node in nodes
|
deployed_node_count = sum(1 for node in nodes
|
||||||
if node.instance.status == 'ACTIVE')
|
if node.instance.status == 'ACTIVE')
|
||||||
@@ -64,7 +65,16 @@ def _get_role_data(plan, stack, form, role):
|
|||||||
if node.instance.status == 'ERROR')
|
if node.instance.status == 'ERROR')
|
||||||
waiting_node_count = (node_count - deployed_node_count -
|
waiting_node_count = (node_count - deployed_node_count -
|
||||||
deploying_node_count - error_node_count)
|
deploying_node_count - error_node_count)
|
||||||
|
if error_node_count:
|
||||||
|
status = 'danger'
|
||||||
|
elif deployed_node_count == data['planned_node_count']:
|
||||||
|
status = 'success'
|
||||||
|
else:
|
||||||
|
status = 'info'
|
||||||
|
|
||||||
data.update({
|
data.update({
|
||||||
|
'status': status,
|
||||||
|
'finished': deployed_node_count == data['planned_node_count'],
|
||||||
'total_node_count': node_count,
|
'total_node_count': node_count,
|
||||||
'deployed_node_count': deployed_node_count,
|
'deployed_node_count': deployed_node_count,
|
||||||
'deploying_node_count': deploying_node_count,
|
'deploying_node_count': deploying_node_count,
|
||||||
@@ -93,6 +103,33 @@ class IndexView(horizon.forms.ModalFormView, StackMixin):
|
|||||||
form_class = forms.EditPlan
|
form_class = forms.EditPlan
|
||||||
success_url = reverse_lazy(INDEX_URL)
|
success_url = reverse_lazy(INDEX_URL)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if request.META.get('HTTP_X_HORIZON_PROGRESS', ''):
|
||||||
|
data = self.get_data(request, {})
|
||||||
|
return http.HttpResponse(json.dumps({
|
||||||
|
'progress': data.get('progress'),
|
||||||
|
'last_failed_events': [{
|
||||||
|
'event_time': event.event_time,
|
||||||
|
'resource_name': event.resource_name,
|
||||||
|
'resource_status': event.resource_status,
|
||||||
|
'resource_statys_reason': event.resource_statys_reason,
|
||||||
|
} for event in data.get('last_failed_events', [])],
|
||||||
|
'roles': [{
|
||||||
|
'status': role.get('status', 'warning'),
|
||||||
|
'finished': role.get('finished', False),
|
||||||
|
'name': role.get('name', ''),
|
||||||
|
'id': role.get('id', ''),
|
||||||
|
'total_node_count': role.get('node_count', 0),
|
||||||
|
'deployed_node_count': role.get('deployed_node_count', 0),
|
||||||
|
'deploying_node_count': role.get('deploying_node_count',
|
||||||
|
0),
|
||||||
|
'waiting_node_count': role.get('waiting_node_count', 0),
|
||||||
|
'error_node_count': role.get('error_node_count', 0),
|
||||||
|
'planned_node_count': role.get('planned_node_count', 0),
|
||||||
|
} for role in data.get('roles', [])],
|
||||||
|
}), mimetype='application/json')
|
||||||
|
return super(IndexView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_form(self, form_class):
|
def get_form(self, form_class):
|
||||||
return form_class(self.request, **self.get_form_kwargs())
|
return form_class(self.request, **self.get_form_kwargs())
|
||||||
|
|
||||||
@@ -104,7 +141,7 @@ class IndexView(horizon.forms.ModalFormView, StackMixin):
|
|||||||
def get_data(self, request, context, *args, **kwargs):
|
def get_data(self, request, context, *args, **kwargs):
|
||||||
plan = api.tuskar.Plan.get_the_plan(request)
|
plan = api.tuskar.Plan.get_the_plan(request)
|
||||||
stack = self.get_stack()
|
stack = self.get_stack()
|
||||||
form = context['form']
|
form = context.get('form')
|
||||||
|
|
||||||
context['plan'] = plan
|
context['plan'] = plan
|
||||||
context['stack'] = stack
|
context['stack'] = stack
|
||||||
@@ -141,19 +178,21 @@ class IndexView(horizon.forms.ModalFormView, StackMixin):
|
|||||||
total_num_nodes_count = max(
|
total_num_nodes_count = max(
|
||||||
resources_count, total_num_nodes_count)
|
resources_count, total_num_nodes_count)
|
||||||
|
|
||||||
context['progress'] = max(
|
context['progress'] = min(95, max(
|
||||||
5, 100 * (total_num_nodes_count - resources_count))
|
5, 100 * float(resources_count) / total_num_nodes_count))
|
||||||
|
elif stack.is_deploying:
|
||||||
|
total = sum(d['total_node_count'] for d in roles)
|
||||||
|
context['progress'] = min(95, max(
|
||||||
|
5, 100 * sum(float(d.get('deployed_node_count', 0))
|
||||||
|
for d in roles) / (total or 1)
|
||||||
|
))
|
||||||
else:
|
else:
|
||||||
# stack is active
|
# stack is active
|
||||||
|
context['progress'] = 100
|
||||||
controller_role = plan.get_role_by_name("controller")
|
controller_role = plan.get_role_by_name("controller")
|
||||||
context['admin_password'] = plan.parameter_value(
|
context['admin_password'] = plan.parameter_value(
|
||||||
controller_role.parameter_prefix + 'AdminPassword')
|
controller_role.parameter_prefix + 'AdminPassword')
|
||||||
|
|
||||||
total = sum(d['total_node_count'] for d in roles)
|
|
||||||
context['progress'] = max(
|
|
||||||
5, 100 * sum(d.get('deployed_node_count', 0)
|
|
||||||
for d in roles) // (total or 1)
|
|
||||||
)
|
|
||||||
context['dashboard_urls'] = stack.dashboard_urls
|
context['dashboard_urls'] = stack.dashboard_urls
|
||||||
else:
|
else:
|
||||||
messages = forms.validate_plan(request, plan)
|
messages = forms.validate_plan(request, plan)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
tuskar.deployment_progress = (function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = {};
|
||||||
|
|
||||||
|
module.init = function () {
|
||||||
|
if (!$('div.deployment-box div.progress')) { return; }
|
||||||
|
module.interval = setInterval(module.check_progress, 30000);
|
||||||
|
module.events_template = Hogan.compile($('#events-template').html() || '');
|
||||||
|
module.roles_template = Hogan.compile($('#roles-template').html() || '');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.check_progress = function () {
|
||||||
|
var $form = $('form.deployment-roles-form');
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
headers: {'X-Horizon-Progress': 'true'},
|
||||||
|
url: $form.attr('action'),
|
||||||
|
dataType: 'json',
|
||||||
|
async: true,
|
||||||
|
success: module.update_progress
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.update_progress = function (data) {
|
||||||
|
if (data.progress >= 100 || data.progress <= 0) {
|
||||||
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
var $bar = $('div.deployment-box div.progress div.progress-bar');
|
||||||
|
$bar.css('width', '' + data.progress + '%');
|
||||||
|
if (data.last_failed_events) {
|
||||||
|
$('div.deploy-last-events').html(module.events_template.render(data));
|
||||||
|
} else {
|
||||||
|
$('div.deploy-last-events').html('');
|
||||||
|
}
|
||||||
|
$('div.deploy-role-status').html(module.roles_template.render(data));
|
||||||
|
};
|
||||||
|
|
||||||
|
horizon.addInitFunction(module.init);
|
||||||
|
return module;
|
||||||
|
} ());
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.menu_formset.js' type='text/javascript' charset='utf-8'></script>
|
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.menu_formset.js' type='text/javascript' charset='utf-8'></script>
|
||||||
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.number_picker.js' type='text/javascript' charset='utf-8'></script>
|
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.number_picker.js' type='text/javascript' charset='utf-8'></script>
|
||||||
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.edit_plan.js' type='text/javascript' charset='utf-8'></script>
|
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.edit_plan.js' type='text/javascript' charset='utf-8'></script>
|
||||||
|
<script src='{{ STATIC_URL }}infrastructure/js/tuskar.deployment_progress.js' type='text/javascript' charset='utf-8'></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% comment %} Tuskar-UI Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %}
|
{% comment %} Tuskar-UI Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %}
|
||||||
|
|||||||
Reference in New Issue
Block a user