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