diff --git a/circle/dashboard/static/dashboard/vm-details.js b/circle/dashboard/static/dashboard/vm-details.js new file mode 100644 index 0000000..bf51564 --- /dev/null +++ b/circle/dashboard/static/dashboard/vm-details.js @@ -0,0 +1,56 @@ + +$(function() { + if($('.timeline .activity:first i:first').hasClass('icon-spin')) + checkNewActivity(); +}); + +function checkNewActivity() { + var latest = $('.activity:first').data('activity-id'); + var latest_sub = $('div[data-activity-id="' + latest + '"] .sub-timeline .sub-activity:first').data('activity-id'); + var instance = location.href.split('/'); instance = instance[instance.length - 2]; + + $.ajax({ + type: 'POST', + url: '/dashboard/vm/' + instance + '/activity/', + headers: {"X-CSRFToken": getCookie('csrftoken')}, + data: {'latest': latest, 'latest_sub': latest_sub}, + success: function(data) { + if(data['new_sub_activities'].length > 0) { + d = data['new_sub_activities']; + html = "" + for(var i=0; i<d.length; i++) { + html += '<div data-activity-id="' + d[i].id + '" class="sub-activity">' + d[i].name + ' - '; + if(d[i].finished != null) { + html += d[i].finished + } else { + html += '<i class="icon-refresh icon-spin" class="sub-activity-loading-icon"></i>'; + } + html += '</div>'; + } + $('div[data-activity-id="' + latest_sub + '"] .sub-activity .sub-activity-loading-icon').remove(); + $('div[data-activity-id="' + latest + '"] .sub-timeline').prepend(html); + } + + if(data['is_parent_finished']) { + var c = "icon-plus" + $('div[data-activity-id="' + latest + '"] .icon-refresh.icon-spin:first').removeClass('icon-refresh').removeClass('icon-spin').addClass(c); + } + + if(data['latest_sub_finished'] != null) { + s = $('div[data-activity-id="' + latest_sub + '"]') + $('.icon-refresh.icon-spin', s).remove(); + $(s).append(data['latest_sub_finished']); + } + + if(data['is_parent_finished']) + return; + else + setTimeout(checkNewActivity, 1000); + }, + error: function() { + + } + }); +} + + diff --git a/circle/dashboard/templates/dashboard/vm-detail-activity.html b/circle/dashboard/templates/dashboard/vm-detail-activity.html index 2bfd925..9ed70f1 100644 --- a/circle/dashboard/templates/dashboard/vm-detail-activity.html +++ b/circle/dashboard/templates/dashboard/vm-detail-activity.html @@ -7,17 +7,27 @@ padding-left: 10px; } </style> + <div class="timeline"> - <div><span class="timeline-icon timeline-warning"><i class="icon-remove"></i></span> <strong>Removing</strong> 2013-11-21 15:32</div> - <div><span class="timeline-icon timeline-warning"><i class="icon-pause"></i></span> <strong>Suspending</strong> 2013-09-21 15:32</div> {% for a in activity %} - <div><span class="timeline-icon"><i class="icon-plus"></i></span> <strong>{{ a.get_readable_name }}</strong> + <div class="activity" data-activity-id="{{ a.pk }}"> + <span class="timeline-icon"> + <i class="{% if not a.finished %} icon-refresh icon-spin {% else %}icon-plus{% endif %}"></i> + </span> + <strong>{{ a.get_readable_name }}</strong> {{ a.started|date:"Y-m-d. H:i" }}, {{ a.user }} {% if a.instanceactivity_set.count > 0 %} <div class="sub-timeline"> - {% for s in a.instanceactivity_set.all %} - {{ s.get_readable_name }} - {{ a.started|time:"H:i:s" }} <br /> - {% endfor %} + {% for s in a.instanceactivity_set.all %} + <div data-activity-id="{{ s.pk }}" class="sub-activity"> + {{ s.get_readable_name }} - + {% if s.finished %} + {{ s.finished|time:"H:i:s" }} + {% else %} + <i class="icon-refresh icon-spin" class="sub-activity-loading-icon"></i> + {% endif %} + </div> + {% endfor %} </div> {% endif %} </div> @@ -29,3 +39,7 @@ <div><span class="timeline-icon"><i class="icon-refresh"></i></span> <strong>Forced reboot</strong> 2013-04-21 15:32, ABC123</div> <div><span class="timeline-icon"><i class="icon-plus"></i></span> <strong>Created</strong> 2013-04-21 15:32, ABC123</div> </div> + +{% block extra_js %} +<script src="{{ STATIC_URL }}dashboard/vm-details.js"></script> +{% endblock %} diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py index dbd4d9a..e79e45a 100644 --- a/circle/dashboard/urls.py +++ b/circle/dashboard/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import patterns, url from vm.models import Instance from .views import ( IndexView, VmDetailView, VmList, VmCreate, TemplateDetail, AclUpdateView, - VmDelete, mass_delete_vm) + VmDelete, mass_delete_vm, vm_activity) urlpatterns = patterns( '', @@ -20,5 +20,6 @@ urlpatterns = patterns( url(r'^vm/delete/(?P<pk>\d+)/$', VmDelete.as_view(), name="dashboard.views.delete-vm"), url(r'^vm/mass-delete/', mass_delete_vm, - name='dashboard.view.mass-delete-vm') + name='dashboard.view.mass-delete-vm'), + url(r'^vm/(?P<pk>\d+)/activity/$', vm_activity) ) diff --git a/circle/dashboard/views.py b/circle/dashboard/views.py index 5775723..1b9b718 100644 --- a/circle/dashboard/views.py +++ b/circle/dashboard/views.py @@ -94,6 +94,7 @@ class VmDetailView(CheckedDetailView): ia = InstanceActivity.objects.filter( instance=self.object, parent=None ).order_by('-started').select_related() + context['activity'] = ia context['acl'] = get_acl_data(instance) return context @@ -329,3 +330,40 @@ def mass_delete_vm(request, **kwargs): messages.success(request, success_message) next = request.GET.get('next') return redirect(next if next else reverse_lazy('dashboard.index')) + + +@require_POST +def vm_activity(request, pk): + latest = request.POST.get('latest') + latest_sub = request.POST.get('latest_sub') + + instance = Instance.objects.get(pk=pk) + new_sub_activities = InstanceActivity.objects.filter( + parent=latest, pk__gt=latest_sub, + instance=instance) + # new_activities = InstanceActivity.objects.filter( + # parent=None, instance=instance, pk__gt=latest).values('finished') + latest_sub_finished = InstanceActivity.objects.get(pk=latest_sub).finished + + time_string = "%H:%M:%S" + new_sub_activities = [ + {'name': a.get_readable_name(), 'id': a.pk, + 'finished': None if a.finished is None else a.finished.strftime( + time_string + ) + } for a in new_sub_activities + ] + + response = { + 'new_sub_activities': new_sub_activities, + # TODO 'new_acitivites': new_activities, + 'is_parent_finished': True if InstanceActivity.objects.get( + pk=latest).finished is not None else False, + 'latest_sub_finished': None if latest_sub_finished is None else + latest_sub_finished.strftime(time_string) + } + + return HttpResponse( + json.dumps(response), + content_type="application/json" + )