From df995e7f9cc2152c96b3278ef4092daedd84ed73 Mon Sep 17 00:00:00 2001 From: Kálmán Viktor <kviktor@cloud.bme.hu> Date: Wed, 26 Feb 2014 18:22:25 +0100 Subject: [PATCH] dashboard: restart, reset and migrate buttons --- circle/dashboard/forms.py | 2 +- circle/dashboard/static/dashboard/dashboard.css | 14 ++++++++++++++ circle/dashboard/static/dashboard/vm-common.js | 21 +++++++++++++++++++++ circle/dashboard/templates/dashboard/_vm-migrate.html | 18 ++++++++++++++++++ circle/dashboard/templates/dashboard/vm-detail.html | 20 ++++++++++++++++++-- circle/dashboard/templates/dashboard/vm-detail/activity.html | 4 ---- circle/dashboard/templates/dashboard/vm-list.html | 3 ++- circle/dashboard/templates/dashboard/vm-list/column-admin.html | 3 +-- circle/dashboard/urls.py | 4 +++- circle/dashboard/views.py | 34 ++++++++++++++++++++++++++++++++++ circle/vm/models/instance.py | 10 +++++----- circle/vm/models/node.py | 4 ++++ 12 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 circle/dashboard/static/dashboard/vm-common.js create mode 100644 circle/dashboard/templates/dashboard/_vm-migrate.html diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py index 8507755..20ab951 100644 --- a/circle/dashboard/forms.py +++ b/circle/dashboard/forms.py @@ -458,7 +458,7 @@ class TemplateForm(forms.ModelForm): template = InstanceTemplate.objects.get(pk=parent) parent = template.__dict__ fields = ["system", "name", "num_cores", "boot_menu", "ram_size", - "priority", "state", "access_method", "raw_data", + "priority", "access_method", "raw_data", "arch", "description"] for f in fields: self.initial[f] = parent[f] diff --git a/circle/dashboard/static/dashboard/dashboard.css b/circle/dashboard/static/dashboard/dashboard.css index 353f035..749fae8 100644 --- a/circle/dashboard/static/dashboard/dashboard.css +++ b/circle/dashboard/static/dashboard/dashboard.css @@ -331,3 +331,17 @@ a.hover-black { display: block; } + + +#vm-migrate-node-list { + list-style: none; +} + +#vm-migrate-node-list li { + padding-bottom: 10px; +} + +.vm-migrate-node-property { + display: block; + padding-left: 15px; +} diff --git a/circle/dashboard/static/dashboard/vm-common.js b/circle/dashboard/static/dashboard/vm-common.js new file mode 100644 index 0000000..665942c --- /dev/null +++ b/circle/dashboard/static/dashboard/vm-common.js @@ -0,0 +1,21 @@ +/* for functions in both vm list and vm detail */ + +$(function() { + + /* vm migrate */ + $('.vm-migrate').click(function(e) { + var vm = $(this).data("vm-pk"); + $.ajax({ + type: 'GET', + url: '/dashboard/vm/' + vm + '/migrate/', + success: function(data) { + $('body').append(data); + $('#create-modal').modal('show'); + $('#create-modal').on('hidden.bs.modal', function() { + $('#create-modal').remove(); + }); + } + }); + return false; + }); +}); diff --git a/circle/dashboard/templates/dashboard/_vm-migrate.html b/circle/dashboard/templates/dashboard/_vm-migrate.html new file mode 100644 index 0000000..b1a93cc --- /dev/null +++ b/circle/dashboard/templates/dashboard/_vm-migrate.html @@ -0,0 +1,18 @@ +{% load i18n %} +{% load sizefieldtags %} + +<form method="POST" action="{% url "dashboard.views.vm-migrate" pk=vm %}"> + {% csrf_token %} + <ul id="vm-migrate-node-list"> + {% for n in nodes %} + <li> + <strong>{{ n }}</strong> + <input type="radio" name="node" value="{{ n.pk }}" style="float: right;"/> + <span class="vm-migrate-node-property">{% trans "CPU load" %}: {{ n.cpu_usage }}</span> + <span class="vm-migrate-node-property">{% trans "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}</span> + <div style="clear: both;"></div> + </li> + {% endfor %} + </ul> + <button type="submit" class="btn btn-primary btn-sm"><i class="icon-truck"></i> Migrate</button> +</form> diff --git a/circle/dashboard/templates/dashboard/vm-detail.html b/circle/dashboard/templates/dashboard/vm-detail.html index 1f25834..91753f5 100644 --- a/circle/dashboard/templates/dashboard/vm-detail.html +++ b/circle/dashboard/templates/dashboard/vm-detail.html @@ -6,7 +6,6 @@ <div class="page-header"> <div class="pull-right" style="padding-top: 15px;"> <a title="Rename" href="#" class="btn btn-default btn-xs vm-details-rename-button"><i class="icon-pencil"></i></a> - <a title="Pause == sleep?" href="#" class="btn btn-default btn-xs"><i class="icon-pause"></i></a> <form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}"> {% csrf_token %} <input type="hidden" name="sleep" value="dummy"/> @@ -27,7 +26,19 @@ <input type="hidden" name="shut_down" value="dummy"/> <button title="{% trans "Shut down" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-off"></i></button> </form> - <a title="Migrate" href="#" class="btn btn-default btn-xs"><i class="icon-truck"></i></a> + <form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}"> + {% csrf_token %} + <input type="hidden" name="reboot" value="dummy"/> + <button title="{% trans "Reboot (ctrl + alt + del)" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-refresh"></i></button> + </form> + <form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}"> + {% csrf_token %} + <input type="hidden" name="reset" value="dummy"/> + <button title="{% trans "Reset (power cycle)" %}" class="btn btn-default btn-xs" type="submit"><i class="icon-bolt"></i></button> + </form> + <a title="Migrate" data-vm-pk="{{ instance.pk }}" href="{% url "dashboard.views.vm-migrate" pk=instance.pk %}" class="btn btn-default btn-xs vm-migrate"> + <i class="icon-truck"></i> + </a> <form style="display: inline;" method="POST" action="{% url "dashboard.views.detail" pk=instance.pk %}"> {% csrf_token %} <input type="hidden" name="save_as" value="dummy"/> @@ -137,3 +148,8 @@ </div> {% endblock %} + +{% block extra_js %} + <script src="{{ STATIC_URL }}dashboard/vm-details.js"></script> + <script src="{{ STATIC_URL }}dashboard/vm-common.js"></script> +{% endblock %} diff --git a/circle/dashboard/templates/dashboard/vm-detail/activity.html b/circle/dashboard/templates/dashboard/vm-detail/activity.html index 178f9f1..c7d4b4d 100644 --- a/circle/dashboard/templates/dashboard/vm-detail/activity.html +++ b/circle/dashboard/templates/dashboard/vm-detail/activity.html @@ -5,7 +5,3 @@ <div id="activity-timeline" class="timeline"> {% include "dashboard/vm-detail/_activity-timeline.html" %} </div> - -{% block extra_js %} -<script src="{{ STATIC_URL }}dashboard/vm-details.js"></script> -{% endblock %} diff --git a/circle/dashboard/templates/dashboard/vm-list.html b/circle/dashboard/templates/dashboard/vm-list.html index 673436b..c6a84ce 100644 --- a/circle/dashboard/templates/dashboard/vm-list.html +++ b/circle/dashboard/templates/dashboard/vm-list.html @@ -72,5 +72,6 @@ {% endblock %} {% block extra_js %} -<script src="{{ STATIC_URL}}dashboard/vm-list.js"></script> + <script src="{{ STATIC_URL}}dashboard/vm-list.js"></script> + <script src="{{ STATIC_URL}}dashboard/vm-common.js"></script> {% endblock %} diff --git a/circle/dashboard/templates/dashboard/vm-list/column-admin.html b/circle/dashboard/templates/dashboard/vm-list/column-admin.html index 026c7fa..3d993ab 100644 --- a/circle/dashboard/templates/dashboard/vm-list/column-admin.html +++ b/circle/dashboard/templates/dashboard/vm-list/column-admin.html @@ -1,5 +1,4 @@ - -<a class="btn btn-default btn-xs" title data-original-title="Migrate"> +<a href="{% url "dashboard.views.vm-migrate" pk=record.pk %}" class="btn btn-default btn-xs vm-migrate" data-vm-pk="{{ record.pk }}" title data-original-title="Migrate"> <i class="icon-truck"></i> </a> <a id="vm-list-rename-button" class="btn btn-default btn-xs" title data-original-title="Rename"> diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py index 396c4b7..336c11c 100644 --- a/circle/dashboard/urls.py +++ b/circle/dashboard/urls.py @@ -8,7 +8,7 @@ from .views import ( TemplateList, LeaseDetail, NodeCreate, LeaseCreate, TemplateCreate, FavouriteView, NodeStatus, GroupList, TemplateDelete, LeaseDelete, VmGraphView, TemplateAclUpdateView, GroupDetailView, GroupDelete, - GroupAclUpdateView, GroupUserDelete, NodeGraphView + GroupAclUpdateView, GroupUserDelete, NodeGraphView, VmMigrateView, ) urlpatterns = patterns( @@ -48,6 +48,8 @@ urlpatterns = patterns( url(r'^vm/mass-delete/', VmMassDelete.as_view(), name='dashboard.view.mass-delete-vm'), url(r'^vm/(?P<pk>\d+)/activity/$', vm_activity), + url(r'^vm/(?P<pk>\d+)/migrate/$', VmMigrateView.as_view(), + name='dashboard.views.vm-migrate'), url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'), url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(), diff --git a/circle/dashboard/views.py b/circle/dashboard/views.py index acec175..5a82371 100644 --- a/circle/dashboard/views.py +++ b/circle/dashboard/views.py @@ -1652,3 +1652,37 @@ class NodeGraphView(SuperuserRequiredMixin, GraphViewBase): def get_object(self, request, pk): return self.model.objects.get(id=pk) + + +class VmMigrateView(SuperuserRequiredMixin, TemplateView): + + def get_template_names(self): + if self.request.is_ajax(): + return ['dashboard/modal-wrapper.html'] + else: + return ['dashboard/nojs-wrapper.html'] + + def get(self, request, form=None, *args, **kwargs): + context = self.get_context_data(**kwargs) + vm = Instance.objects.get(pk=kwargs['pk']) + context.update({ + 'template': 'dashboard/_vm-migrate.html', + 'box_title': _('Migrate %(name)s' % {'name': vm.name}), + 'ajax_title': True, + 'vm': kwargs['pk'], + 'nodes': [n for n in Node.objects.filter(enabled=True) + if n.state == "ONLINE"] + }) + return self.render_to_response(context) + + def post(self, *args, **kwargs): + node = self.request.POST.get("node") + vm = Instance.objects.get(pk=kwargs['pk']) + + if node: + node = Node.objects.get(pk=node) + vm.migrate_async(to_node=node, user=self.request.user) + else: + messages.error(self.request, _("You didn't select a node!")) + + return redirect("%s#activity" % vm.get_absolute_url()) diff --git a/circle/vm/models/instance.py b/circle/vm/models/instance.py index 54074f9..3887b83 100644 --- a/circle/vm/models/instance.py +++ b/circle/vm/models/instance.py @@ -907,15 +907,15 @@ class Instance(AclBase, VirtualMachineDescModel, TimeStampedModel): task_uuid=task_uuid, user=user): queue_name = self.get_remote_queue_name('vm') - vm_tasks.restart.apply_async(args=[self.vm_name], - queue=queue_name - ).get(timeout=timeout) + vm_tasks.reset.apply_async(args=[self.vm_name], + queue=queue_name + ).get(timeout=timeout) def reset_async(self, user=None): """Execute reset asynchronously. """ - return local_tasks.restart.apply_async(args=[self, user], - queue="localhost.man") + return local_tasks.reset.apply_async(args=[self, user], + queue="localhost.man") def reboot(self, user=None, task_uuid=None, timeout=5): """Reboot virtual machine with Ctrl+Alt+Del signal. diff --git a/circle/vm/models/node.py b/circle/vm/models/node.py index e44f21a..7dd6f4f 100644 --- a/circle/vm/models/node.py +++ b/circle/vm/models/node.py @@ -235,6 +235,10 @@ class Node(TimeStampedModel): def ram_usage(self): return float(self.get_monitor_info()["memory.usage"]) / 100 + @property + def byte_ram_usage(self): + return self.ram_usage * self.ram_size + def update_vm_states(self): """Update state of Instances running on this Node. -- libgit2 0.26.0