diff --git a/instances/views.py b/instances/views.py index 30bd83b..162745a 100644 --- a/instances/views.py +++ b/instances/views.py @@ -1,7 +1,9 @@ +import time +import json from string import letters, digits from random import choice from bisect import insort -from django.http import HttpResponseRedirect +from django.http import HttpResponse, HttpResponseRedirect from django.core.urlresolvers import reverse from django.shortcuts import render from django.utils.translation import ugettext_lazy as _ @@ -407,4 +409,123 @@ def instance(request, compute_id, vname): error_messages.append(lib_err.message) addlogmsg(request.user.id, instance.id, lib_err.message) - return render(request, 'instance.html', locals()) \ No newline at end of file + return render(request, 'instance.html', locals()) + + +def inst_graph(request, compute_id, vname): + """ + Return instance usage + """ + if not request.user.is_authenticated(): + return HttpResponseRedirect(reverse('login')) + + datasets = {} + datasets_rd = [] + datasets_wr = [] + json_blk = [] + cookie_blk = {} + blk_error = False + datasets_rx = [] + datasets_tx = [] + json_net = [] + cookie_net = {} + net_error = False + networks = None + disks = None + points = 5 + curent_time = time.strftime("%H:%M:%S") + compute = Compute.objects.get(id=compute_id) + + try: + conn = wvmInstance(compute.hostname, + compute.login, + compute.password, + compute.type, + vname) + status = conn.get_status() + cpu_usage = conn.cpu_usage() + blk_usage = conn.disk_usage() + net_usage = conn.net_usage() + conn.close() + except libvirtError: + status = None + blk_usage = None + cpu_usage = None + net_usage = None + + if status == 1: + cookies = request.COOKIES + if cookies.get('cpu') == '{}' or not cookies.get('cpu') or not cpu_usage: + datasets['cpu'] = [0] + datasets['timer'] = [curent_time] + else: + datasets['cpu'] = eval(cookies.get('cpu')) + datasets['timer'] = eval(cookies.get('timer')) + + datasets['timer'].append(curent_time) + datasets['cpu'].append(int(cpu_usage['cpu'])) + + if len(datasets['timer']) > points: + datasets['timer'].pop(0) + if len(datasets['cpu']) > points: + datasets['cpu'].pop(0) + + for blk in blk_usage: + if cookies.get('hdd') == '{}' or not cookies.get('hdd') or not blk_usage: + datasets_wr.append(0) + datasets_rd.append(0) + else: + datasets['hdd'] = eval(cookies.get('hdd')) + try: + datasets_rd = datasets['hdd'][blk['dev']][0] + datasets_wr = datasets['hdd'][blk['dev']][1] + except: + blk_error = True + + if not blk_error: + datasets_rd.append(int(blk['rd']) / 1048576) + datasets_wr.append(int(blk['wr']) / 1048576) + + if len(datasets_rd) > points: + datasets_rd.pop(0) + if len(datasets_wr) >= points + 1: + datasets_wr.pop(0) + + json_blk.append({'dev': blk['dev'], 'data': [datasets_rd, datasets_wr]}) + cookie_blk[blk['dev']] = [datasets_rd, datasets_wr] + + for net in net_usage: + if cookies.get('net') == '{}' or not cookies.get('net') or not net_usage: + datasets_rx.append(0) + datasets_tx.append(0) + else: + datasets['net'] = eval(cookies.get('net')) + try: + datasets_rx = datasets['net'][net['dev']][0] + datasets_tx = datasets['net'][net['dev']][1] + except: + net_error = True + + if not net_error: + datasets_rx.append(int(net['rx']) / 1048576) + datasets_tx.append(int(net['tx']) / 1048576) + + if len(datasets_rx) > points: + datasets_rx.pop(0) + if len(datasets_tx) > points: + datasets_tx.pop(0) + + json_net.append({'dev': net['dev'], 'data': [datasets_rx, datasets_tx]}) + cookie_net[net['dev']] = [datasets_rx, datasets_tx] + + data = json.dumps({'status': status, 'cpudata': datasets['cpu'], 'hdddata': json_blk, 'netdata': json_net, 'timeline': datasets['timer']}) + + response = HttpResponse() + response['Content-Type'] = "text/javascript" + if status == 1: + response.cookies['cpu'] = datasets['cpu'] + response.cookies['timer'] = datasets['timer'] + response.cookies['hdd'] = cookie_blk + response.cookies['net'] = cookie_net + response.write(data) + return response \ No newline at end of file diff --git a/static/css/webvirtcloud.css b/static/css/webvirtcloud.css index e579349..6c2b8d1 100644 --- a/static/css/webvirtcloud.css +++ b/static/css/webvirtcloud.css @@ -100,7 +100,7 @@ body { width: 65px; } -#action-button { +.action-button { text-align: center; } diff --git a/templates/instance.html b/templates/instance.html index b1564ca..f83909e 100644 --- a/templates/instance.html +++ b/templates/instance.html @@ -38,43 +38,43 @@ <!-- Nav tabs --> <ul class="nav nav-pills" role="tablist"> <li role="presentation" class="active"> - <a href="#power" id="action-button" aria-controls="power" role="tab" data-toggle="tab"> + <a href="#power" class="action-button" aria-controls="power" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-off" aria-hidden="true"></span> {% trans "Power" %} </a> </li> <li role="presentation"> - <a href="#access" id="action-button" aria-controls="access" role="tab" data-toggle="tab"> + <a href="#access" class="action-button" aria-controls="access" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-lock" aria-hidden="true"></span> {% trans "Access" %} </a> </li> <li role="presentation"> - <a href="#resize" id="action-button" aria-controls="resize" role="tab" data-toggle="tab"> + <a href="#resize" class="action-button" aria-controls="resize" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> {% trans "Resize" %} </a> </li> <li role="presentation"> - <a href="#snapshots" id="action-button" aria-controls="snapshots" role="tab" data-toggle="tab"> + <a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span> {% trans "Snapshots" %} </a> </li> <li role="presentation"> - <a href="#settings" id="action-button" aria-controls="settings" role="tab" data-toggle="tab"> + <a href="#settings" class="action-button" aria-controls="settings" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-cog" aria-hidden="true"></span> {% trans "Settings" %} </a> </li> <li role="presentation"> - <a href="#graphics" id="action-button" aria-controls="graphics" role="tab" data-toggle="tab"> + <a href="#graphics" id="chartgraphs" class="action-button" aria-controls="graphics" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-signal" aria-hidden="true"></span> {% trans "Graphs" %} </a> </li> <li role="presentation"> - <a href="#undefine" id="action-button" aria-controls="undefine" role="tab" data-toggle="tab"> + <a href="#undefine" class="action-button" aria-controls="undefine" role="tab" data-toggle="tab"> <span id="action-block" class="glyphicon glyphicon-trash" aria-hidden="true"></span> {% trans "Destroy" %} </a> @@ -689,15 +689,55 @@ <!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> - <a href="#graps" aria-controls="graps" role="tab" data-toggle="tab"> + <a href="#graphs" aria-controls="graphs" role="tab" data-toggle="tab"> {% trans "Real Time" %} </a> </li> </ul> <!-- Tab panes --> <div class="tab-content"> - <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graps"> - <p>Graphics</p> + <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="graphs"> + <div class="panel panel-success"> + <div class="panel-heading"> + <h3 class="panel-title"><i class="fa fa-long-arrow-right"></i> {% trans "CPU usage" %}</h3> + </div> + <div class="panel-body"> + <div class="flot-chart"> + <div class="flot-chart-content" id="flot-moving-line-chart" style="padding: 0px; position: relative;"> + <canvas id="cpuChart" width="735" height="250"></canvas> + </div> + </div> + </div> + </div> + {% for net in networks %} + <div class="panel panel-info"> + <div class="panel-heading"> + <h3 class="panel-title"><i class="fa fa-long-arrow-right"></i> {% trans "Bandwidth device:" %} eth{{ forloop.counter0 }}</h3> + </div> + <div class="panel-body"> + <div class="flot-chart"> + <div class="flot-chart-content" id="flot-moving-line-chart" style="padding: 0px; position: relative;"> + <canvas id="netEth{{ forloop.counter0 }}Chart" width="735" height="250"></canvas> + </div> + </div> + </div> + </div> + {% endfor %} + {% for disk in disks %} + <div class="panel panel-warning"> + <div class="panel-heading"> + <h3 class="panel-title"><i class="fa fa-long-arrow-right"></i> {% trans "Disk I/O device:" %} {{ disk.dev }}</h3> + </div> + <div class="panel-body"> + <div class="flot-chart"> + <div class="flot-chart-content" id="flot-moving-line-chart" style="padding: 0px; position: relative;"> + <canvas id="blk{{ disk.dev }}Chart" width="735" height="250"></canvas> + </div> + </div> + </div> + </div> + {% endfor %} + <div class="clearfix"></div> </div> </div> </div> @@ -828,4 +868,126 @@ }); }); </script> +<script src="{{ STATIC_URL }}js/Chart.min.js"></script> +<script> +$( "#graphics" ).click(); +</script> +<script> + $('#chartgraphs').on('shown.bs.tab', function (e) { + var cpuLineData = { + labels : [0, 0, 0, 0, 0], + datasets : [ + { + fillColor: "rgba(241,72,70,0.5)", + strokeColor: "rgba(241,72,70,1)", + pointColor : "rgba(241,72,70,1)", + pointStrokeColor : "#fff", + pointHighlightFill : "#fff", + pointHighlightStroke : "rgba(220,220,220,1)", + data : [0, 0, 0, 0, 0] + } + ] + }; + + var diskLineData = { + labels : [0, 0, 0, 0, 0], + datasets : [ + { + "fillColor": "rgba(83,191,189,0.5)", + "strokeColor": "rgba(83,191,189,1)", + "pointColor": "rgba(83,191,189,1)", + "pointStrokeColor": "#fff", + "data": [0, 0, 0, 0, 0] + }, + { + "fillColor": "rgba(249,134,33,0.5)", + "strokeColor": "rgba(249,134,33,1)", + "pointColor": "rgba(249,134,33,1)", + "pointStrokeColor": "#fff", + "data": [0, 0, 0, 0, 0] + }, + ] + } + + var netLineData = { + labels : [0, 0, 0, 0, 0], + datasets : [ + { + "fillColor": "rgba(83,191,189,0.5)", + "strokeColor": "rgba(83,191,189,1)", + "pointColor": "rgba(83,191,189,1)", + "pointStrokeColor": "#fff", + "data": [0, 0, 0, 0, 0] + }, + { + "fillColor": "rgba(151,187,205,0.5)", + "strokeColor": "rgba(151,187,205,1)", + "pointColor": "rgba(151,187,205,1)", + "pointStrokeColor": "#fff", + "data": [0, 0, 0, 0, 0] + }, + ] + } + + var cpuOpt = { + animation: false, + pointDotRadius: 2, + scaleLabel: "<%=value%> %", + scaleOverride: true, + scaleSteps: 10, + scaleStepWidth: 10, + scaleStartValue: 0, + responsive: true + }; + + var diskOpt = { + animation: false, + pointDotRadius: 2, + scaleLabel: "<%=value%> Mb/s", + responsive: true + }; + + var netOpt = { + animation: false, + pointDotRadius: 2, + scaleLabel: "<%=value%> Mbps", + responsive: true + }; + + var cpu_ctx = $("#cpuChart").get(0).getContext("2d"); + var cpuChart = new Chart(cpu_ctx).Line(cpuLineData, cpuOpt); + + var diskChart = {}; + {% for disk in disks %} + var disk_ctx_{{ disk.dev }} = $("#blk{{ disk.dev }}Chart").get(0).getContext("2d"); + diskChart['{{ disk.dev }}'] = new Chart(disk_ctx_{{ disk.dev }}).Line(diskLineData, diskOpt); + {% endfor %} + + var netChart = {}; + {% for net in networks %} + var net_ctx_{{ forloop.counter0 }} = $("#netEth{{ forloop.counter0 }}Chart").get(0).getContext("2d"); + netChart['{{ forloop.counter0 }}'] = new Chart(net_ctx_{{ forloop.counter0 }}).Line(netLineData, netOpt); + {% endfor %} + + function graph_usage() { + $.getJSON('{% url 'inst_graph' compute_id vname %}', function (data) { + cpuChart.scale.xLabels = data.timeline; + for (var i = 0; i < 5; i++) { + cpuChart.datasets[0].points[i].value = data.cpudata[i]; + } + cpuChart.update(); + }); + } + +{##} +{# $(function(f) {#} +{# window.setInterval('graph_usage()', 2000);#} +{# });#} + + }); + + + + + </script> {% endblock %} \ No newline at end of file diff --git a/templates/overview.html b/templates/overview.html index 0c29937..afc110b 100644 --- a/templates/overview.html +++ b/templates/overview.html @@ -81,7 +81,7 @@ </div> {% endblock %} {% block script %} -<script src="{{ STATIC_URL }}/js/Chart.min.js"></script> +<script src="{{ STATIC_URL }}js/Chart.min.js"></script> <script> var cpuLineData = { labels : [0, 0, 0, 0, 0], @@ -133,7 +133,7 @@ responsive: true }); - function hostusage() { + function graph_usage() { $.getJSON('{% url 'compute_graph' compute_id %}', function (data) { cpuChart.scale.xLabels = data.timeline; memChart.scale.xLabels = data.timeline; @@ -145,8 +145,9 @@ memChart.update(); }); } + $(function () { - window.setInterval('hostusage()', 4000); + window.setInterval(graph_usage(), 4000); }); </script> {% endblock %} \ No newline at end of file diff --git a/webvirtcloud/urls.py b/webvirtcloud/urls.py index 6e50c72..d4ca5f2 100644 --- a/webvirtcloud/urls.py +++ b/webvirtcloud/urls.py @@ -12,6 +12,8 @@ urlpatterns = patterns('', url(r'^instances$', 'instances.views.instances', name='instances'), url(r'^instance/(\d+)/([\w\-\.]+)/$', 'instances.views.instance', name='instance'), + url(r'^instance/statistics/(\d+)/([\w\-\.]+)/$', 'instances.views.inst_graph', name='inst_graph'), + url(r'^computes/$', 'computes.views.computes', name='computes'), url(r'^compute/overview/(\d+)/$', 'computes.views.overview', name='overview'),