From 40738d792017e4a7642733c47a7512c20adc74a7 Mon Sep 17 00:00:00 2001
From: Bandic007 <r.v.mirchev@gmail.com>
Date: Thu, 27 Sep 2018 11:54:11 +0300
Subject: [PATCH] Additional changes

---
 computes/templates/overview.html           |   3 +
 computes/urls.py                           |  23 ++-
 computes/views.py                          |  22 +++
 console/templates/console-vnc-full.html    |   4 +-
 console/templates/console-vnc-lite.html    |   3 +-
 console/views.py                           |   5 +-
 instances/templates/compute_instances.html | 199 +++++++++++++++++++++
 instances/templates/instances_grouped.html |   1 +
 instances/views.py                         |  13 +-
 interfaces/templates/interfaces.html       |   5 +-
 networks/templates/networks.html           |   5 +-
 nwfilters/templates/nwfilters.html         |   3 +
 secrets/templates/secrets.html             |   7 +-
 static/js/sortable.min.js                  |   2 +-
 storages/templates/storage.html            |   2 +-
 storages/templates/storages.html           |   5 +-
 webvirtcloud/urls.py                       |   6 +
 17 files changed, 289 insertions(+), 19 deletions(-)
 create mode 100644 instances/templates/compute_instances.html

diff --git a/computes/templates/overview.html b/computes/templates/overview.html
index 89251cc..466c10e 100644
--- a/computes/templates/overview.html
+++ b/computes/templates/overview.html
@@ -11,6 +11,9 @@
                         <li class="active">
                             <i class="fa fa-dashboard"></i> {% trans "Overview" %}
                         </li>
+                        <li>
+                            <i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
+                        </li>
                         <li>
                             <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
                         </li>
diff --git a/computes/urls.py b/computes/urls.py
index 58a0497..4b4ab6d 100644
--- a/computes/urls.py
+++ b/computes/urls.py
@@ -1,9 +1,22 @@
 from django.conf.urls import url
-from . import views
+from storages.views import storages, storage
+from networks.views import networks, network
+from secrets.views import secrets
+from create.views import create_instance
+from interfaces.views import interfaces, interface
+from computes.views import overview, compute_graph
+from instances.views import instances
 
 urlpatterns = [
-    url(r'^$', views.computes, name='computes'),
-    url(r'^overview/(?P<compute_id>[0-9]+)/$', views.overview, name='overview'),
-    url(r'^statistics/(?P<compute_id>[0-9]+)/$',
-        views.compute_graph, name='compute_graph'),
+    url(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
+    url(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
+    url(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='compute_instances'),
+    url(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
+    url(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
+    url(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
+    url(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
+    url(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
+    url(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
+    url(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
+    url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
 ]
diff --git a/computes/views.py b/computes/views.py
index 18ad680..93e3f5e 100644
--- a/computes/views.py
+++ b/computes/views.py
@@ -134,6 +134,28 @@ def computes(request):
                     error_messages.append(msg_err.as_text())
     return render(request, 'computes.html', locals())
 
+@login_required
+def compute_instances(request, compute_id):
+    """
+    :param request:
+    :return:
+    """
+    if not request.user.is_superuser:
+        return HttpResponseRedirect(reverse('index'))
+    error_messages = []
+    compute = get_object_or_404(Compute, pk=compute_id)
+    try:
+        conn = wvmHostDetails(compute.hostname,
+                              compute.login,
+                              compute.password,
+                              compute.type)
+        hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
+        hypervisor = conn.hypervisor_type()
+        mem_usage = conn.get_memory_usage()
+        conn.close()
+    except libvirtError as lib_err:
+        error_messages.append(lib_err)
+    return render(request, 'compute_instances.html', locals())
 
 @login_required
 def overview(request, compute_id):
diff --git a/console/templates/console-vnc-full.html b/console/templates/console-vnc-full.html
index 6149289..ff6ee98 100755
--- a/console/templates/console-vnc-full.html
+++ b/console/templates/console-vnc-full.html
@@ -233,7 +233,7 @@
                                     </li>
                                      <li>
                                         <label for="noVNC_setting_host">Host:</label>
-                                        <input id="noVNC_setting_host" value="{{ ws_host }}"/>
+                                        <input id="noVNC_setting_host" value="{{ ws_host }}{{ ws_path }}"/>
                                     </li>
                                     <li>
                                         <label for="noVNC_setting_port">Port:</label>
@@ -332,4 +332,4 @@
         <source src="{% static "js/novnc/app/sounds/bell.oga" %}" type="audio/ogg">
         <source src="{% static "js/novnc/app/sounds/bell.mp3" %}" type="audio/mpeg">
     </audio>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/console/templates/console-vnc-lite.html b/console/templates/console-vnc-lite.html
index 4dd7bfe..0502a62 100755
--- a/console/templates/console-vnc-lite.html
+++ b/console/templates/console-vnc-lite.html
@@ -220,6 +220,7 @@
         //var port = WebUtil.getConfigVar('port', window.location.port);
         var host = '{{ ws_host }}';
         var port = '{{ ws_port }}';
+	var wspath = '{{ ws_path }}';
 
         // if port == 80 (or 443) then it won't be present and should be
         // set manually
@@ -263,7 +264,7 @@
                 url = 'ws';
             }
 
-            url += '://' + host;
+            url += '://' + host + wspath;
             if(port) {
                 url += ':' + port;
             }
diff --git a/console/views.py b/console/views.py
index 4196ff0..70d0186 100644
--- a/console/views.py
+++ b/console/views.py
@@ -7,6 +7,8 @@ from instances.models import Instance
 from vrtManager.instance import wvmInstance
 from webvirtcloud.settings import WS_PORT
 from webvirtcloud.settings import WS_PUBLIC_HOST
+from webvirtcloud.settings import WS_PUBLIC_PORT
+from webvirtcloud.settings import WS_PUBLIC_PATH
 from libvirt import libvirtError
 
 
@@ -40,8 +42,9 @@ def console(request):
         console_websocket_port = None
         console_passwd = None
 
-    ws_port = console_websocket_port if console_websocket_port else WS_PORT
+    ws_port = console_websocket_port if console_websocket_port else WS_PUBLIC_PORT
     ws_host = WS_PUBLIC_HOST if WS_PUBLIC_HOST else request.get_host()
+    ws_path = WS_PUBLIC_PATH if WS_PUBLIC_PATH else '/'
 
     if ':' in ws_host:
         ws_host = re.sub(':[0-9]+', '', ws_host)
diff --git a/instances/templates/compute_instances.html b/instances/templates/compute_instances.html
new file mode 100644
index 0000000..e96c763
--- /dev/null
+++ b/instances/templates/compute_instances.html
@@ -0,0 +1,199 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load staticfiles %}
+{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
+{% block style %}
+    <link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
+{% endblock %}
+{% block content %}
+    <!-- Page Heading -->
+                <div class="row">
+                    <div class="col-lg-12">
+                        {% if request.user.is_superuser %}
+                            {% include 'create_inst_block.html' %}
+                        {% endif %}
+                        {% if all_host_vms or all_user_vms %}
+                            <div class="pull-right search">
+                                <input id="filter" class="form-control" type="text" placeholder="Search">
+                            </div>
+                        {% endif %}
+                        <h1 class="page-header">{{ compute.name }}</h1>
+                    </div>
+                </div>
+            <div class="row">
+                <div class="col-lg-12">
+
+                    <ol class="breadcrumb">
+                        <li class="active">
+                            <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
+                        </li>
+                        <li>
+                            <i class="fa fa-server"></i> {% trans "Instances" %}
+                        </li>
+                        <li>
+                            <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
+                        </li>
+                        <li>
+                            <i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
+                        </li>
+                        <li>
+                            <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
+                        </li>
+                        <li>
+                            <i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
+                        </li>
+                        <li>
+                            <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
+                        </li>
+                    </ol>
+                </div>
+            </div>
+            <!-- /.row -->
+
+            {% include 'errors_block.html' %}
+            <div class="row">
+                {% if not  all_host_vms %}
+                    <div class="col-lg-12">
+                        <div class="alert alert-warning alert-dismissable">
+                            <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
+                            <i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any instances" %}
+                        </div>
+                    </div>
+                {% else %}
+                    <div class="col-lg-12">
+                        <table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
+                            <thead>
+                            <tr>
+                                <th>Name<br>Description</th>
+                                <th>Host<br>User</th>
+                                <th>Status</th>
+                                <th>VCPU</th>
+                                <th>Memory</th>
+                                <th data-sortable="false" style="width:205px;">Actions</th>
+                            </tr>
+                            </thead>
+                            <tbody class="searchable">
+                            {% for host, inst  in all_host_vms.items %}
+                                {% for vm, info in inst.items %}
+                                    <tr>
+                                        <td><a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br><small><em>{{ info.title }}</em></small></td>
+                                        <td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a><br><small><em>{% if info.userinstances.count > 0 %}{{ info.userinstances.first_user.user.username }}{% if info.userinstances.count > 1 %} (+{{ info.userinstances.count|add:"-1" }}){% endif %}{% endif %}</em></small></td>
+                                        <td>
+                                            {% ifequal info.status 1 %}<span class="text-success">{% trans "Active" %}</span>{% endifequal %}
+                                            {% ifequal info.status 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endifequal %}
+                                            {% ifequal info.status 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endifequal %}
+                                        </td>
+                                        <td>{{ info.vcpu }}</td>
+                                        <td>{{ info.memory|filesizeformat }}</td>
+                                        <td><form action="" method="post" role="form">{% csrf_token %}
+                                            <input type="hidden" name="name" value="{{ vm }}"/>
+                                            <input type="hidden" name="compute_id" value="{{ host.0 }}"/>
+                                            {% ifequal info.status 5 %}
+                                                {% if info.is_template %}
+                                                    <button class="btn btn-sm btn-default" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ host.0 }}, '{{ vm }}');">
+                                                        <span class="glyphicon glyphicon-duplicate"></span>
+                                                    </button>
+                                                {% else %}
+                                                    <button class="btn btn-sm btn-default" type="submit" name="poweron" title="{% trans "Power On" %}">
+                                                        <span class="glyphicon glyphicon-play"></span>
+                                                    </button>
+                                                {% endif %}
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Suspend" %}">
+                                                    <span class="glyphicon glyphicon-pause"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
+                                                    <span class="glyphicon glyphicon-off"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
+                                                    <span class="glyphicon glyphicon-refresh"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
+                                                    <span class="glyphicon glyphicon-eye-open"></span>
+                                                </button>
+                                            {% endifequal %}
+                                            {% ifequal info.status 3 %}
+                                                <button class="btn btn-sm btn-default" type="submit" name="resume" title="{% trans "Resume" %}">
+                                                    <span class="glyphicon glyphicon-play"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Suspend" %}">
+                                                    <span class="glyphicon glyphicon-pause"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
+                                                    <span class="glyphicon glyphicon-off"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
+                                                    <span class="glyphicon glyphicon-refresh"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
+                                                    <span class="glyphicon glyphicon-eye-open"></span>
+                                                </button>
+                                            {% endifequal %}
+                                            {% ifequal info.status 1 %}
+                                                <button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}">
+                                                    <span class="glyphicon glyphicon-play"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default" type="submit" name="suspend" title="{% trans "Suspend" %}">
+                                                    <span class="glyphicon glyphicon-pause"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default" type="submit" name="poweroff" title="{% trans "Power Off" %}" onclick="return confirm('Are you sure?')">
+                                                    <span class="glyphicon glyphicon-off"></span>
+                                                </button>
+                                                <button class="btn btn-sm btn-default" type="submit" name="powercycle" title="{% trans "Power Cycle" %}" onclick="return confirm('Are you sure?')">
+                                                    <span class="glyphicon glyphicon-refresh"></span>
+                                                </button>
+                                                <a href="#" class="btn btn-sm btn-default" onclick='open_console("{{ host.0 }}-{{ info.uuid }}")' title="{% trans "Console" %}">
+                                                    <span class="glyphicon glyphicon-eye-open"></span>
+                                                </a>
+                                            {% endifequal %}
+                                        </form>
+                                        </td>
+                                    </tr>
+                                {% endfor %}
+                            {% endfor %}
+                            </tbody>
+                        </table>
+                     </div>
+                {% endif %}
+            </div>
+{% endblock %}
+{% block script %}
+<script src="{% static "js/sortable.min.js" %}"></script>
+<script>
+    function open_console(uuid) {
+        window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=485");
+    }
+</script>
+<script>
+    function filter_table() {
+        var rex = new RegExp($(this).val(), 'i');
+        $('.searchable tr').hide();
+        $('.searchable tr').filter(function () {
+            return rex.test($(this).text());
+        }).show();
+        Cookies.set("instances_filter", $(this).val(), { expires: 1 });
+    }
+    $(document).ready(function () {
+        instances_filter_cookie = Cookies.get("instances_filter");
+        if (instances_filter_cookie) {
+            $('#filter').val(instances_filter_cookie);
+            $('#filter').each(filter_table);
+        }
+        (function ($) {
+            $('#filter').keyup(filter_table)
+        }(jQuery));
+    });
+</script>
+<script>
+    function goto_instance_clone(compute, instance) {
+        window.location = "/instance/" + compute + "/" + instance + "/#clone";
+    }
+</script>
+{% if request.user.is_superuser %}
+    <script>
+        function goto_compute() {
+            var compute = $("#compute_select").val();
+            window.location = "/compute/" + compute + "/create/";
+        }
+    </script>
+{% endif %}
+{% endblock %}
diff --git a/instances/templates/instances_grouped.html b/instances/templates/instances_grouped.html
index 2e129c2..0d16056 100644
--- a/instances/templates/instances_grouped.html
+++ b/instances/templates/instances_grouped.html
@@ -15,6 +15,7 @@
     {% for host, inst  in all_host_vms.items %}
         <tr class="active" style="font-weight: bold;border-bottom: 2px solid darkgray;border-top: 2px solid darkgray;">
             <td>
+                <span style="text-align:center;">{{ inst.items|length }}</span>
                 <span id="collapse_host_instances_{{ host.1 }}" class="glyphicon glyphicon-chevron-up" onclick="hide_host_instances('{{ host.1 }}');"></span>
             </td>
             <td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a></td>
diff --git a/instances/views.py b/instances/views.py
index 70fb9bf..19db3fb 100644
--- a/instances/views.py
+++ b/instances/views.py
@@ -38,7 +38,7 @@ def index(request):
 
 
 @login_required
-def instances(request):
+def instances(request, compute_id=None):
     """
     :param request:
     :return:
@@ -47,7 +47,11 @@ def instances(request):
     error_messages = []
     all_host_vms = {}
     all_user_vms = {}
-    computes = Compute.objects.all().order_by("name")
+
+    if not compute_id:
+        computes = Compute.objects.all().order_by("name")
+    else:
+        computes = [Compute.objects.get(id=compute_id),]
 
     def get_userinstances_info(instance):
         info = {}
@@ -195,8 +199,11 @@ def instances(request):
 
     view_style = settings.VIEW_INSTANCES_LIST_STYLE
 
-    return render(request, 'instances.html', locals())
+    if compute_id:
+        compute = computes[0]
+        return render(request, 'compute_instances.html', locals())
 
+    return render(request, 'instances.html', locals())
 
 @login_required
 def instance(request, compute_id, vname):
diff --git a/interfaces/templates/interfaces.html b/interfaces/templates/interfaces.html
index 58d4978..c121595 100644
--- a/interfaces/templates/interfaces.html
+++ b/interfaces/templates/interfaces.html
@@ -12,6 +12,9 @@
                         <li class="active">
                             <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
                         </li>
+                        <li class="active">
+                            <i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
+                        </li>
                         <li>
                             <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
                         </li>
@@ -94,4 +97,4 @@
         }).change();
     });
 </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/networks/templates/networks.html b/networks/templates/networks.html
index 2acc1f3..5e843ae 100644
--- a/networks/templates/networks.html
+++ b/networks/templates/networks.html
@@ -11,6 +11,9 @@
                         <li class="active">
                             <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
                         </li>
+                        <li class="active">
+                            <i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
+                        </li>
                         <li>
                             <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
                         </li>
@@ -80,4 +83,4 @@
         }).change();
     });
 </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/nwfilters/templates/nwfilters.html b/nwfilters/templates/nwfilters.html
index e237380..9fd228f 100644
--- a/nwfilters/templates/nwfilters.html
+++ b/nwfilters/templates/nwfilters.html
@@ -12,6 +12,9 @@
                         <li class="active">
                             <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
                         </li>
+                        <li>
+                            <i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
+                        </li>
                         <li>
                             <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
                         </li>
diff --git a/secrets/templates/secrets.html b/secrets/templates/secrets.html
index 07b104a..28d8dbd 100644
--- a/secrets/templates/secrets.html
+++ b/secrets/templates/secrets.html
@@ -10,11 +10,14 @@
             <div class="row">
                 <div class="col-lg-12">
                     {% include 'create_secret_block.html' %}
-                    <h1 class="page-header">{% trans "Secrets" %}</h1>
+                    <h1 class="page-header">{{ compute.name }}</h1>
                     <ol class="breadcrumb">
                         <li class="active">
                             <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
                         </li>
+                        <li class="active">
+                            <i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
+                        </li>
                         <li>
                             <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
                         </li>
@@ -122,4 +125,4 @@
 {% endblock %}
 {% block script %}
 <script src="{% static "js/sortable.min.js" %}"></script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/static/js/sortable.min.js b/static/js/sortable.min.js
index 8278f50..b88c451 100755
--- a/static/js/sortable.min.js
+++ b/static/js/sortable.min.js
@@ -1,2 +1,2 @@
 /*! sortable.js 0.8.0 */
-(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
\ No newline at end of file
+(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
diff --git a/storages/templates/storage.html b/storages/templates/storage.html
index 4d3c706..77e45e0 100644
--- a/storages/templates/storage.html
+++ b/storages/templates/storage.html
@@ -45,7 +45,7 @@
         <p>{% trans "Pool type:" %}</p>
         <p>{% trans "Pool path:" %}</p>
         <p>{% trans "Pool status:" %}</p>
-        <p>{% trans "Size:" %} ({{ size|filesizeformat }} / {{ used|filesizeformat }})</p>
+        <p>{% trans "Size:" %} (Used: {{ used|filesizeformat }} / From total: {{ size|filesizeformat }})</p>
         <p>{% trans "State:" %}</p>
         <p>{% trans "Autostart:" %}</p>
     </div>
diff --git a/storages/templates/storages.html b/storages/templates/storages.html
index 026fe13..20f147c 100644
--- a/storages/templates/storages.html
+++ b/storages/templates/storages.html
@@ -11,6 +11,9 @@
                         <li class="active">
                             <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
                         </li>
+                        <li class="active">
+                            <i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
+                        </li>
                         <li>
                             <i class="fa fa-hdd-o"></i> {% trans "Storages" %}
                         </li>
@@ -67,4 +70,4 @@
                     {% endfor %}
                 {% endif %}
             </div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/webvirtcloud/urls.py b/webvirtcloud/urls.py
index 79992af..2aa2c58 100644
--- a/webvirtcloud/urls.py
+++ b/webvirtcloud/urls.py
@@ -8,6 +8,7 @@ from create.views import create_instance
 from interfaces.views import interfaces, interface
 from console.views import console
 from nwfilters.views import nwfilters, nwfilter
+from computes.views import computes
 # from django.contrib import admin
 
 urlpatterns = [
@@ -15,8 +16,13 @@ urlpatterns = [
     url(r'^instances/$', instances, name='instances'),
 
     url(r'^instance/', include('instances.urls')),
+    url(r'^instances/$', instances, name='instances'),
+
     url(r'^accounts/', include('accounts.urls')),
+
     url(r'^computes/', include('computes.urls')),
+    url(r'^computes/', computes, name='computes'),
+
     url(r'^logs/', include('logs.urls')),
     url(r'^datasource/', include('datasource.urls')),