Merge pull request #486 from tiwariav/task/3728/vm_teminate_animation

task/3728 vm termination animation
This commit is contained in:
Arvind Tiwari 2017-09-23 23:26:00 +05:30 committed by GitHub
commit ae1a317bf9
8 changed files with 263 additions and 89 deletions

View file

@ -561,6 +561,9 @@ msgstr "Aktueller Preis"
msgid "Your VM is"
msgstr "Deine VM ist"
msgid "Terminating"
msgstr "Beenden"
msgid "Pending"
msgstr "In Vorbereitung"
@ -573,6 +576,11 @@ msgstr "Fehlgeschlagen"
msgid "Terminate VM"
msgstr "VM Beenden"
msgid "Sorry, there was an unexpected error. Kindly retry."
msgstr ""
"Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. "
"Versuche es doch bitte noch einmal."
msgid "Something doesn't work?"
msgstr "Etwas funktioniert nicht?"
@ -591,6 +599,11 @@ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
msgid "OK"
msgstr ""
#, python-format
msgid ""
"Your Virtual Machine %(virtual_machine.name)s is successfully terminated!"
msgstr "Deine Virtuelle Machine (VM) %(virtual_machine.name)s wurde erfolgreich beendet!"
msgid "Virtual Machines"
msgstr "Virtuelle Maschinen"
@ -678,12 +691,20 @@ msgid ""
"contact Data Center Light Support."
msgstr "Kontaktiere den Data Center Light Support."
msgid "Terminated"
msgstr "Beendet"
msgid "Error terminating VM"
msgstr "Fehler beenden VM"
msgid "Virtual Machine Cancellation"
msgstr "VM Kündigung"
#, python-format
msgid "VM %(VM_ID)s terminated successfully"
msgstr "VM %(VM_ID)s erfolgreich beendet"
#~ msgid "Close"
#~ msgstr "Schliessen"
#~ msgid "VM %(VM_ID)s terminated successfully"
#~ msgstr "VM %(VM_ID)s erfolgreich beendet"
#~ msgid "days"
#~ msgstr "tage"
@ -760,9 +781,6 @@ msgstr "VM %(VM_ID)s erfolgreich beendet"
#~ msgid "Ipv6"
#~ msgstr "IPv6"
#~ msgid "Close"
#~ msgstr "Schliessen"
#~ msgid "Cancel"
#~ msgstr "Beenden"

View file

@ -139,6 +139,10 @@
.modal-text p:not(:last-of-type){
margin-bottom: 5px;
}
.modal-title + .modal-footer {
margin-top: 5px;
}
.modal-footer {
border-top: 0px solid #e5e5e5;
width: 100%;

View file

@ -853,6 +853,9 @@ a.list-group-item-danger:focus,
.panel-danger > .panel-heading {
color: #eb4d5c;
}
.alert-danger{
background: rgba(235, 204, 209, 0.2);
}
.has-error .form-control,
.has-error .input-group-addon {
color: #eb4d5c;

View file

@ -290,6 +290,11 @@
text-align: center;
}
.vm-vmid .alert {
margin-top: 15px;
margin-bottom: -60px;
}
.vm-item-lg {
font-size: 22px;
margin-top: 5px;
@ -305,6 +310,10 @@
color: #e47f2f;
}
.vm-color-failed {
color: #eb4d5c;
}
.vm-detail-item .value{
font-weight: 400;
}
@ -512,7 +521,7 @@
color: #87B6EA;
}
.vm-status, .vm-status-active, .vm-status-failed {
.vm-status, .vm-status-active, .vm-status-failed, .vm-status-pending {
font-weight: 600;
}
.vm-status-active {
@ -521,6 +530,9 @@
.vm-status-failed {
color: #eb4d5c;
}
.vm-status-pending {
color: #e47f2f;
}
@media (min-width:768px) {
.dashboard-subtitle {
@ -627,4 +639,28 @@
left: auto;
right: 8px;
}
}
.processing > .btn {
position: relative;
border-color: #eee;
}
.processing > .btn:hover,
.processing > .btn:focus,
.processing > .btn:active {
border-color: #eee;
}
.processing > .btn:after {
content: ' ';
display: block;
position: absolute;
background-image: url('/static/hosting/img/ajax-loader.gif');
background-repeat: no-repeat;
background-position: center;
background-color: #eee;
width: 100%;
top: 0;
height: 100%;
left: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

View file

@ -1,4 +1,84 @@
$(document).ready(function () {
function VMTerminateStatus($container, url) {
$.ajax({
url: url,
type: 'GET',
dataType: 'json',
success: function(data) {
VMTerminateSuccess($container, data);
},
error: function() {
setTimeout(function(){
VMTerminateStatus($container, url);
}, 5000);
}
});
}
function VMTerminateActive($container, altText) {
$container.find('.alert-danger').addClass('hide');
$container.addClass('processing')
.find('.vm-item-lg').attr('class', '')
.addClass('vm-item-lg vm-color-failed')
.text(altText);
$container.find('.btn').prop('disabled', true);
$('#confirm-cancel').modal('hide');
}
function VMTerminateSuccess($container, data) {
$container.addClass('terminate-success')
.find('.vm-item-lg').text(data.text);
$container.find('.btn').remove();
$('#terminate-success').modal('show');
}
function VMTerminateFail($container, data, text) {
$container.addClass('terminate-fail')
.find('.vm-item-lg').text(text);
$container.find('.btn').prop('disabled', false);
$container.find('.alert-danger').text(data.text).removeClass('hide');
$container.removeClass('processing');
}
$(document).ready(function() {
$('#confirm-cancel').on('click', '.btn-ok', function(e) {
var url = $('#virtual_machine_cancel_form').attr('action');
var $container = $('#terminate-VM');
var text = $container.find('.vm-item-lg').text();
var altText = $container.attr('data-alt');
VMTerminateActive($container, altText);
$.post(url)
.done(function(data) {
if (data.status == true) {
VMTerminateSuccess($container, data);
} else {
if ('text' in data) {
VMTerminateFail($container, data, text);
} else {
VMTerminateStatus($container, url);
}
}
})
.fail(function(data) {
if (data.status==504) {
VMTerminateStatus($container, url);
} else {
VMTerminateFail($container, data, text);
}
})
});
var hash = window.location.hash;
hash && $('ul.nav a[href="' + hash + '"]').tab('show');
$('.nav-tabs a').click(function(e) {
$(this).tab('show');
var scrollmem = $('body').scrollTop() || $('html').scrollTop();
window.location.hash = this.hash;
$('html,body').scrollTop(scrollmem);
});
$('.modal-text').removeClass('hide');
var create_vm_form = $('#virtual_machine_create_form');
create_vm_form.submit(function () {
@ -32,19 +112,4 @@ $(document).ready(function () {
});
return false;
});
$('#confirm-cancel').on('click', '.btn-ok', function (e) {
$('#virtual_machine_cancel_form').trigger('submit');
});
var hash = window.location.hash;
hash && $('ul.nav a[href="' + hash + '"]').tab('show');
$('.nav-tabs a').click(function (e) {
$(this).tab('show');
var scrollmem = $('body').scrollTop() || $('html').scrollTop();
window.location.hash = this.hash;
$('html,body').scrollTop(scrollmem);
});
});

View file

@ -53,19 +53,26 @@
<h2 class="vm-detail-title">{% trans "Status" %} <img src="{% static 'hosting/img/connected.svg' %}" class="un-icon"></h2>
<div class="vm-vmid">
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
{% if virtual_machine.state == 'PENDING' %}
<div class="vm-item-lg vm-color-pending">{% trans "Pending" %}</div>
{% elif virtual_machine.state == 'ACTIVE' %}
<div class="vm-item-lg vm-color-online">{% trans "Online" %}</div>
{% elif virtual_machine.state == 'FAILED'%}
<div class="vm-item-lg vm-color-failed">{% trans "Failed" %}</div>
{% endif %}
{% if not virtual_machine.status == 'canceled' %}
<form method="POST" id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}">
{% csrf_token %}
</form>
<button data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-vm-term">{% trans "Terminate VM" %}</button>
{% endif %}
<div id="terminate-VM" data-alt="{% trans 'Terminating' %}">
{% if virtual_machine.state == 'PENDING' %}
<div class="vm-item-lg vm-color-pending">{% trans "Pending" %}</div>
{% elif virtual_machine.state == 'ACTIVE' %}
<div class="vm-item-lg vm-color-online">{% trans "Online" %}</div>
{% elif virtual_machine.state == 'FAILED'%}
<div class="vm-item-lg vm-color-failed">{% trans "Failed" %}</div>
{% else %}
<div class="vm-item-lg"></div>
{% endif %}
{% if not virtual_machine.status == 'canceled' %}
<form method="POST" id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}">
{% csrf_token %}
</form>
<button data-toggle="modal" data-target="#confirm-cancel" class="btn btn-vm-term">{% trans "Terminate VM" %}</button>
<div class="alert alert-danger hide">
{% trans "Sorry, there was an unexpected error. Kindly retry." %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
@ -89,24 +96,41 @@
</div>
<!-- Cancel Modal -->
<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Confirm"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<div class="modal-body">
<div class="modal-icon"><i class="fa fa-ban" aria-hidden="true"></i></div>
<h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine"%}</h4>
<h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine" %}</h4>
<div class="modal-text">
<p>{% trans "Do you want to cancel your Virtual Machine" %} ?</p>
<p><strong>{{virtual_machine.name}}</strong></p>
</div>
<div class="modal-footer">
<a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a>
</div>
</div>
</div>
</div>
</div>
<!-- / Cancel Modal -->
<!-- Success Modal -->
<div class="modal fade" id="terminate-success" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body">
<div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div>
<h4 class="modal-title" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</h4>
<div class="modal-footer">
<a href="{% url 'hosting:virtual_machines' %}" class="btn btn-success btn-wide">{% trans "OK" %}</a>
</div>
</div>
</div>
</div>
</div>
<!-- / Cancel Modal -->
{%endblock%}

View file

@ -1,6 +1,7 @@
import json
import logging
import uuid
from time import sleep
from django import forms
from django.conf import settings
@ -10,16 +11,17 @@ from django.contrib.auth.tokens import default_token_generator
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse_lazy, reverse
from django.http import Http404
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import redirect
from django.shortcuts import render
from django.http import Http404, HttpResponseRedirect, HttpResponse
from django.shortcuts import redirect, render
from django.utils.http import urlsafe_base64_decode
from django.utils.safestring import mark_safe
from django.utils.translation import get_language, ugettext_lazy as _
from django.views.generic import View, CreateView, FormView, ListView, \
DetailView, \
DeleteView, TemplateView, UpdateView
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.views.generic import (
View, CreateView, FormView, ListView, DetailView, DeleteView,
TemplateView, UpdateView
)
from guardian.mixins import PermissionRequiredMixin
from oca.pool import WrongIdError
from stored_messages.api import mark_read
@ -35,8 +37,9 @@ from utils.forms import BillingAddressForm, PasswordResetRequestForm, \
UserBillingAddressForm
from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \
LoginViewMixin
from utils.views import (
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
)
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
UserHostingKeyForm, generate_ssh_key_name
from .mixins import ProcessVMSelectionMixin
@ -985,7 +988,19 @@ class VirtualMachineView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
vm = self.get_object()
if vm is None:
return redirect(reverse('hosting:virtual_machines'))
if self.request.is_ajax():
storage = messages.get_messages(request)
for m in storage:
pass
storage.used = True
return HttpResponse(
json.dumps({'text': ugettext('Terminated')}),
content_type="application/json"
)
else:
return redirect(reverse('hosting:virtual_machines'))
elif self.request.is_ajax():
return HttpResponse()
try:
serializer = VirtualMachineSerializer(vm)
context = {
@ -1000,6 +1015,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
response = {'status': False}
owner = self.request.user
vm = self.get_object()
@ -1009,42 +1025,50 @@ class VirtualMachineView(LoginRequiredMixin, View):
email=owner.email,
password=owner.password
)
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
terminated = manager.delete_vm(
vm.id
)
try:
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
except WrongIdError:
return redirect(reverse('hosting:virtual_machines'))
terminated = manager.delete_vm(vm.id)
if not terminated:
messages.error(
request,
'Error terminating VM %s' % (opennebula_vm_id)
)
return HttpResponseRedirect(self.get_success_url())
context = {
'vm': vm_data,
'base_url': "{0}://{1}".format(self.request.scheme,
self.request.get_host()),
'page_header': _('Virtual Machine Cancellation')
}
email_data = {
'subject': context['page_header'],
'to': self.request.user.email,
'context': context,
'template_name': 'vm_canceled',
'template_path': 'hosting/emails/',
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
}
email = BaseEmail(**email_data)
email.send()
messages.error(
request,
_('VM %(VM_ID)s terminated successfully') % {
'VM_ID': opennebula_vm_id}
response['text'] = ugettext(
'Error terminating VM') + opennebula_vm_id
else:
for t in range(15):
try:
manager.get_vm(opennebula_vm_id)
except WrongIdError:
response['status'] = True
response['text'] = ugettext('Terminated')
break
except BaseException:
break
else:
sleep(2)
context = {
'vm': vm_data,
'base_url': "{0}://{1}".format(self.request.scheme,
self.request.get_host()),
'page_header': _('Virtual Machine Cancellation')
}
email_data = {
'subject': context['page_header'],
'to': self.request.user.email,
'context': context,
'template_name': 'vm_canceled',
'template_path': 'hosting/emails/',
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
}
email = BaseEmail(**email_data)
email.send()
return HttpResponse(
json.dumps(response),
content_type="application/json"
)
return HttpResponseRedirect(self.get_success_url())
class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin,
ListView):