1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-02-23 21:57:43 +03:00

Feature #1098: Add accounting info to Self-Service dashboard

(cherry picked from commit 934ba8ae2edca081727ca2bc74c12e0eb5306c14)
This commit is contained in:
Hector Sanjuan 2012-08-30 19:27:53 +02:00 committed by Ruben S. Montero
parent 048ddc6624
commit e6dad7ab3e
9 changed files with 209 additions and 34 deletions

View File

@ -570,4 +570,59 @@ class OCCIServer < CloudServer
return vnc.proxy(vm)
end
##########################################################################
# Accounting
##########################################################################
def get_user_accounting(options)
tstart = options[:start].to_i
tend = options[:end].to_i
interval = options[:interval].to_i
meters = options[:monitor_resources]
acct_options = {:start_time => tstart,
:end_time => tend}
info_flag = Pool::INFO_ALL
result = {}
meters_a = meters.split(',')
meters_a.each do | meter |
result[meter] = []
end
pool = VirtualMachinePool.new(@client)
acct_xml = pool.accounting_xml(info_flag, acct_options)
if OpenNebula.is_error?(acct_xml)
error = Error.new(acct_xml.message)
return [500, error.to_json]
end
xml = XMLElement.new
xml.initialize_xml(acct_xml, 'HISTORY_RECORDS')
while tstart < tend
tstep = tstart + interval
count = Hash.new
xml.each("HISTORY[STIME<=#{tstep} and ETIME=0 or STIME<=#{tstep} and ETIME>=#{tstart}]") do |hr|
meters_a.each do | meter |
count[meter] ||= 0
count[meter] += hr["VM/#{meter}"].to_i if hr["VM/#{meter}"]
end
end
count.each do | mname, mcount |
result[mname] << [tstart, mcount]
end
tstart = tstep
end
return [200, {:monitoring => result}.to_json]
end
end

View File

@ -443,4 +443,8 @@ post '/ui/startvnc/:id' do
@occi_server.startvnc(vm_id, settings.vnc)
end
get '/ui/accounting' do
@occi_server.get_user_accounting(params)
end
Sinatra::Application.run!

View File

@ -133,7 +133,7 @@ $(document).ready(function () {
applyDefaultStyles: false
, center__paneSelector: ".outer-center"
, west__paneSelector: ".outer-west"
, west__size: 181
, west__size: 200
, north__size: 26
, south__size: 26
, spacing_open: 0 // ALL panes

View File

@ -255,22 +255,18 @@ var OCCI = {
callback_error(request, OCCI.Error(response)) : null;
}
});
}
/*
"monitor": function(params,resource,all){
},
"accounting": function(params, resource){
var callback = params.success;
var callback_error = params.error;
var data = params.data;
var method = "monitor";
var action = OpenNebula.Helper.action(method);
var request = OpenNebula.Helper.request(resource,method, data);
var url = resource.toLowerCase();
url = all ? url + "/monitor" : url + "/" + params.data.id + "/monitor";
var method = "accounting";
var request = OCCI.Helper.request(resource, method, data);
$.ajax({
url: url,
url: 'ui/accounting',
type: "GET",
data: data['monitor'],
dataType: "json",
@ -279,11 +275,10 @@ var OCCI = {
},
error: function(response){
return callback_error ?
callback_error(request, OpenNebula.Error(response)) : null;
callback_error(request, OCCI.Error(response)) : null;
}
});
}
*/
},
"Auth": {
@ -600,7 +595,9 @@ var OCCI = {
},
"show" : function(params){
OCCI.Action.show(params,OCCI.User.resource);
},
"accounting" : function(params){
OCCI.Action.accounting(params, OCCI.User.resource);
}
}
}

View File

@ -14,6 +14,17 @@
/* limitations under the License. */
/* -------------------------------------------------------------------------- */
var user_acct_graphs = [
{ title : tr("CPU"),
monitor_resources : "CPU",
humanize_figures : false
},
{ title : tr("Memory"),
monitor_resources : "MEMORY",
humanize_figures : true
}
];
var dashboard_tab_content =
'<table id="dashboard_table">\
<tr>\
@ -47,6 +58,41 @@ var dashboard_tab_content =
<tr id="network_quotas"><td class="key_td">'+tr("Networks")+':</td>\
<td class="value_td">'+$network_count+'</td></tr>\
</table>\
\
</div>\
</div>\
</td>\
</tr>\
<tr>\
<td>\
<div class="panel" id="user_acct_panel">\
<h3>' + tr("Usages") + '<i class="icon-refresh user_acct_date_ok" style="float:right;cursor:pointer"></i></h3>\
<div class="panel_info">\
\
<div style="margin-left:20px;text-align:center;">\
'+tr("From")+':&nbsp;<input type="text" style="font-size:12px;width: 80px;" id="user_acct_from" name="from"/>&nbsp;&nbsp;\
'+tr("To")+':&nbsp;<input type="text" style="font-size:12px;width: 80px;" id="user_acct_to" name="to"/>\
&nbsp;&nbsp;<a href="#" class="user_acct_date_ok">'+tr('Go!')+' <i class="icon-ok"></i></a>\
</div>\
\
<table class="info_table">\
<tr><td id="legend_CPU"></td></tr>\
<tr><td style="border:0;width:800px!important;">\
<div id="user_acct_CPU" style="height:100px;position:relative;left:0px;overflow: hidden;">'+
spinner+
'</div>\
</td>\
</tr>\
</table>\
<table class="info_table">\
<tr><td id="legend_MEMORY"></td></tr>\
<tr><td style="border:0;width:800px!important;">\
<div id="user_acct_MEMORY" style="height:100px;position:relative;left:0px;overflow: hidden;">'+
spinner+
'</div>\
</td>\
</tr>\
</table>\
\
</div>\
</div>\
@ -136,15 +182,18 @@ Sunstone.addAction('User.refresh', {
}
});
Sunstone.addAction('User.accounting', {
type: "monitor",
call: OCCI.User.accounting,
callback: function(req, response){
var info = req.request.data[0].monitor;
plot_graph(response, '#user_acct_panel', 'user_acct_', info);
},
error: onError
});
Sunstone.addMainTab('dashboard_tab',dashboard_tab);
function quickstart_setup(){
$('#dashboard_table #quickstart_form input',main_tabs_context).click(function(){
Sunstone.runAction($(this).val());
});
};
function generateDashboardLinks(){
var links="<ul>";
for (var i=0; i<dashboard_links.length;i++){
@ -282,7 +331,7 @@ function dashboardQuotasHTML(req, response){
//puts the dashboard values into "retrieving"
function emptyDashboard(){
$("#dashboard_tab .value_td span",main_tabs_context).html(spinner);
$("#dashboard_tab .value_td > span",main_tabs_context).html(spinner);
}
@ -304,6 +353,60 @@ function updateDashboard(what,json_info){
}
}
function accountingSetup(){
var context = $('#dashboard_table', main_tabs_context);
//Enable datepicker
$("#user_acct_from", context).datepicker({
defaultDate: "-1d",
changeMonth: true,
numberOfMonths: 1,
dateFormat: "dd/mm/yy",
defaultDate: '-1',
onSelect: function( selectedDate ) {
$( "#user_acct_to", context).datepicker("option",
"minDate",
selectedDate );
}
});
$("#user_acct_from", context).datepicker('setDate', '-1');
$("#user_acct_to", context).datepicker({
defaultDate: "0",
changeMonth: true,
numberOfMonths: 1,
dateFormat: "dd/mm/yy",
maxDate: '+1',
onSelect: function( selectedDate ) {
$( "#user_acct_from", context).datepicker( "option",
"maxDate",
selectedDate );
}
});
$("#user_acct_to", context).datepicker('setDate', 'Now');
//Listen to set date button
$('.user_acct_date_ok', context).click(function(){
var from = $("#user_acct_from", context).val();
var to = $("#user_acct_to", context).val();
var start = $.datepicker.parseDate('dd/mm/yy', from)
if (start){
start = start.getTime();
start = Math.floor(start / 1000);
}
var end = $.datepicker.parseDate('dd/mm/yy', to);
if (end){
end = end.getTime();
end = Math.floor(end / 1000);
}
loadAccounting('User', null, user_acct_graphs,
{ start : start, end: end });
return false;
});
}
$(document).ready(function(){
//Dashboard link listener
@ -332,7 +435,7 @@ $(document).ready(function(){
emptyDashboard();
quickstart_setup();
accountingSetup();
$('#li_dashboard_tab').click(function(){
hideDialog();
@ -347,4 +450,5 @@ $(document).ready(function(){
}
});
Sunstone.runAction("User.show", uid);
loadAccounting('User', null, user_acct_graphs);
});

View File

@ -18,7 +18,12 @@
<script type="text/javascript" src="vendor/jQueryLayout/jquery.layout-latest.min.js"></script>
<script type="text/javascript" src="vendor/dataTables/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="vendor/fileuploader/fileuploader.js"></script>
<script type="text/javascript" src="vendor/flot/jquery.flot.min.js"></script>
<!-- <script type="text/javascript" src="vendor/flot/jquery.flot.pie.min.js"></script> -->
<script type="text/javascript" src="vendor/flot/jquery.flot.resize.min.js"></script>
<!--[if lte IE 8]>
<script type="text/javascript" src="vendor/explorercanvas/excanvas.compiled.js"></script>
<![endif]-->
<!-- End Vendor Libraries -->
<!--Languages-->

View File

@ -295,8 +295,6 @@ class SunstoneServer < CloudServer
end
# this code is meant to be replaced as accouting functionality
# is moved to OCA. Filtering by group should perhaps be added then.
# returns a { monitoring : meter1 : [[ts1, agg_value],[ts2, agg_value]...]
# meter2 : [[ts1, agg_value],[ts2, agg_value]...]}
# with this information we can paint historical graphs of usage
@ -310,11 +308,15 @@ class SunstoneServer < CloudServer
acct_options = {:start_time => tstart,
:end_time => tend}
# If we want acct per group, we ask for all VMs visible to user
# and then filter by group.
if gid
uid = Pool::INFO_ALL
acct_options[:group] = gid
end
# Init results and request accounting
result = {}
meters_a = meters.split(',')
meters_a.each do | meter |
@ -331,11 +333,17 @@ class SunstoneServer < CloudServer
xml = XMLElement.new
xml.initialize_xml(acct_xml, 'HISTORY_RECORDS')
# We aggregate the accounting values for each interval withing
# the given timeframe
while tstart < tend
tstep = tstart + interval
count = Hash.new
# We count machines which have started before the end of
# this interval AND have not finished yet OR machines which
# have started before the end of this interval AND
# have finished anytime after the start of this interval
xml.each("HISTORY[STIME<=#{tstep} and ETIME=0 or STIME<=#{tstep} and ETIME>=#{tstart}]") do |hr|
meters_a.each do | meter |
@ -344,6 +352,8 @@ class SunstoneServer < CloudServer
end
end
# We have aggregated values for this interval
# Then we just add them to the results along with a timestamp
count.each do | mname, mcount |
result[mname] << [tstart, mcount]
end

View File

@ -348,7 +348,6 @@ var OpenNebula = {
var data = params.data;
var method = "monitor";
var action = OpenNebula.Helper.action(method);
var request = OpenNebula.Helper.request(resource,method, data);
var url = resource.toLowerCase();

View File

@ -621,25 +621,26 @@ function escapeDoubleQuotes(string){
//will be contained. They have some elements which ids are
//determined by the graphs configuration, so when the time
//of plotting comes, we can put the data in the right place.
function generateMonitoringDivs(graphs, id_prefix){
function generateMonitoringDivs(graphs, id_prefix, options){
var str = "";
//40% of the width of the screen minus
//181px (left menu size)
//200px (left menu size)
var width = ($(window).width()-200)*39/100;
var id_suffix="";
var label="";
var id="";
var omit_title = options && options.omit_title;
$.each(graphs,function(){
label = this.monitor_resources;
id_suffix=label.replace(/,/g,'_');
id_suffix=id_suffix.replace(/\//g,'_');
id = id_prefix+id_suffix;
str+='<table class="info_table">\
<thead><tr><th colspan="1">'+this.title+'</th></tr></thead>\
<tr><td id="legend_'+id_suffix+'"></td></tr>\
<tr><td style="border:0">\
<div id="'+id+'" style="width:'+width+'px; height:150px;margin-bottom:10px;position:relative;left:0px;">'+
str+='<table class="info_table">'+
(!omit_title ? '<thead><tr><th colspan="1">'+this.title+'</th></tr></thead>' : '')
+ '<tr><td id="legend_'+id_suffix+'"></td></tr>\
<tr><td style="border:0;width:100%;">\
<div id="'+id+'" style="width:'+width+'px; height:150px;position:relative;left:0px;margin: 0 auto 10px auto">'+
spinner+
'</div>\
</td></tr></table>';