1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-23 22:50:09 +03:00

Feature #650: General completion and refactoring of single host and VMs monitoring:

*Fixed bugs on server. Enabled requesting several resources at once.
*Correct css for .info_tables
*Add graphs to hosts and VMs info tabs. Correct vm_log method so dialog is not regenerated (and graphs destroyed with it). Improved the common methods for creating the divs containing grahps and plotting the information.
*Improved OneMonitor tool. Add total, active and error counts to the global logs.
This commit is contained in:
Hector Sanjuan 2011-05-27 03:48:11 +02:00 committed by Ruben S. Montero
parent 728268d802
commit 3a54ee1ba5
11 changed files with 186 additions and 69 deletions

View File

@ -265,7 +265,7 @@ class SunstoneServer
#
############################################################################
def get_log(resource,id,config,monitor_res,history_length)
def get_log(resource,id,config,monitor_resources,history_length)
log_file_prefix = case resource
when "vm","VM"
config[:host_log_file]
@ -279,32 +279,46 @@ class SunstoneServer
log_file = "#{log_file_prefix}_#{id}.csv"
first_line = `head -1 #{log_file}`
first_line = `head -1 #{log_file}`.chomp
if $?.exitstatus != 0
then
error = Error.new("Cannot open log file")
return [500, error.to_json]
end
fields = first_line.split(',')
poll_time_pos = fields.index("time")
resource_pos = fields.index(monitor_res)
id_pos = fields.index("id")
graph = []
tail = `tail -#{history_length} #{log_file}`
tail.each_line do | line |
line_arr = line.delete('"').split(',')
if (line_arr[id_pos].to_i == id.to_i)
then
graph << [ line_arr[poll_time_pos].to_i*1000, line_arr[resource_pos].to_i ]
end
n_lines = `wc -l #{log_file} | cut -d' ' -f 1`.to_i
if n_lines <= history_length.to_i
history_length = n_lines-1
end
return graph.to_json
fields = first_line.split(',')
poll_time_pos = fields.index("time")
id_pos = fields.index("id")
if !id_pos or !poll_time_pos
error = Error.new("It seems poll_time or id information cannot be read from log file")
return [500, error.to_json]
end
series = [] #will hold several graphs
tail = `tail -#{history_length} #{log_file}`
monitor_resources.split(',').each do | resource |
graph = []
resource_pos = fields.index(resource)
tail.each_line do | line |
line_arr = line.delete('"').split(',')
if (line_arr[id_pos].to_i == id.to_i)
graph << [ line_arr[poll_time_pos].to_i*1000, line_arr[resource_pos].to_i ]
end
end
series << graph
end
return series.to_json
end
############################################################################

View File

@ -362,7 +362,7 @@ tr.even:hover{
vertical-align:top;
}
.info_table th,h3 {
.info_table > thead th,h3 {
border-bottom: 2px solid #353735;
color: #353735;
font-size: 14px;
@ -371,7 +371,7 @@ tr.even:hover{
}
.info_table td{
.info_table > tbody > tr > td{
border-bottom: 1px solid #CCCCCC;
color: #353735;
padding-top: 6px;

View File

@ -17,6 +17,22 @@
/*Host tab plugin*/
var HOST_HISTORY_LENGTH = 40;
var host_graphs = [
{
title : "CPU Monitoring information",
monitor_resources : "cpu_usage,used_cpu,max_cpu",
humanize_figures : false,
history_length : HOST_HISTORY_LENGTH
},
{
title: "Memory monitoring information",
monitor_resources : "mem_usage,used_mem,max_mem",
humanize_figures : true,
history_length : HOST_HISTORY_LENGTH
}
]
var hosts_tab_content =
'<form id="form_hosts" action="javascript:alert(\'js errors?!\')">\
@ -205,14 +221,9 @@ var host_actions = {
type: "monitor",
call : OpenNebula.Host.monitor,
callback: function(req,response) {
var info = req.request.data[0];
var label = info.monitor.monitor_res;
//remove spinner
$('#host_monitoring_tab .loading_img').parent().remove();
var info = req.request.data[0].monitor;
plot_graph(response,'#host_monitoring_tab',
'host_monitor_'+label,label);
'host_monitor_',info);
},
error: onError
},
@ -583,7 +594,7 @@ function updateHostInfo(request,host){
var monitor_tab = {
title: "Monitoring information",
content : generateMonitoringDivs(["cpu_usage","mem_usage"],"host_monitor_")
content : generateMonitoringDivs(host_graphs,"host_monitor_")
}
//Sunstone.updateInfoPanelTab(info_panel_name,tab_name, new tab object);
@ -593,8 +604,9 @@ function updateHostInfo(request,host){
Sunstone.popUpInfoPanel("host_info_panel");
//pop up panel while we retrieve the graphs
Sunstone.runAction("Host.monitor",host_info.ID,{history_length: HOST_HISTORY_LENGTH, monitor_res: "cpu_usage"});
Sunstone.runAction("Host.monitor",host_info.ID,{history_length: HOST_HISTORY_LENGTH, monitor_res: "mem_usage"});
for (var i=0; i<host_graphs.length; i++){
Sunstone.runAction("Host.monitor",host_info.ID,host_graphs[i]);
};
}

View File

@ -16,12 +16,38 @@
/*Virtual Machines tab plugin*/
var INCLUDE_URI = "vendor/noVNC/include/";
var VM_HISTORY_LENGTH = 40;
function loadVNC(){
var script = '<script src="vendor/noVNC/include/vnc.js"></script>';
document.write(script);
}
loadVNC();
var vm_graphs = [
{ title : "CPU",
monitor_resources : "cpu",
humanize_figures : false,
history_length : VM_HISTORY_LENGTH
},
{ title : "Memory",
monitor_resources : "memory",
humanize_figures : true,
history_length : VM_HISTORY_LENGTH
},
{ title : "Network transmission",
monitor_resources : "net_tx",
humanize_figures : true,
history_length : VM_HISTORY_LENGTH
},
{ title : "Network reception",
monitor_resources : "net_rx",
humanize_figures : true,
history_length : VM_HISTORY_LENGTH
},
]
var vms_tab_content =
'<form id="virtualMachine_list" action="javascript:alert(\'js error!\');">\
<div class="action_blocks">\
@ -305,13 +331,7 @@ var vm_actions = {
colored_log += line + "\n";
}
var log_tab = {
title: "VM log",
content: '<pre>'+colored_log+'</pre>'
}
Sunstone.updateInfoPanelTab("vm_info_panel","vm_log_tab",log_tab);
Sunstone.popUpInfoPanel("vm_info_panel",0);
$('#vm_log_tab#').html('<pre>'+colored_log+'</pre>')
},
error: function(request,error_json){
$("#vm_log pre").html('');
@ -333,6 +353,17 @@ var vm_actions = {
callback: null,
error: onError,
notify: true
},
"VM.monitor" : {
type: "monitor",
call : OpenNebula.VM.monitor,
callback: function(req,response) {
var info = req.request.data[0].monitor;
plot_graph(response,'#vm_monitoring_tab',
'vm_monitor_',info);
},
error: onError
}
}
@ -624,30 +655,36 @@ function updateVMInfo(request,vm){
</tr>\
</table>'
}
var template_tab = {
title: "VM Template",
content: '<table id="vm_template_table" class="info_table">\
<thead><tr><th colspan="2">VM template</th></tr></thead>'+
prettyPrintJSON(vm_info.TEMPLATE)+
'</table>'
'</table>'
}
var log_tab = {
title: "VM log",
content: '<pre>'+spinner+'</pre>'
content: '<div>'+spinner+'</div>'
}
Sunstone.updateInfoPanelTab("vm_info_panel","vm_info_tab",info_tab);
var monitoring_tab = {
title: "Monitoring information",
content: generateMonitoringDivs(vm_graphs,"vm_monitor_")
}
Sunstone.updateInfoPanelTab("vm_info_panel","vm_info_tab",info_tab);
Sunstone.updateInfoPanelTab("vm_info_panel","vm_template_tab",template_tab);
Sunstone.updateInfoPanelTab("vm_info_panel","vm_log_tab",log_tab);
//Here it is special, as we will let the callback from the VM.log
//action popUp the info panel again when the info is received.
Sunstone.updateInfoPanelTab("vm_info_panel","vm_monitoring_tab",monitoring_tab);
//Pop up the info panel and asynchronously get vm_log and stats
Sunstone.popUpInfoPanel("vm_info_panel");
Sunstone.runAction("VM.log",vm_info.ID);
for (var i=0; i<vm_graphs.length; i++){
Sunstone.runAction("VM.monitor",vm_info.ID,vm_graphs[i]);
};
}
// Sets up the create-template dialog and all the processing associated to it,

View File

@ -16,7 +16,7 @@
/* Some useful functions for Sunstone default plugins */
var INTERVAL=60000;
var INTERVAL=60000; //milisecs
function someTime(){
return Math.floor(Math.random()*30000);
@ -478,30 +478,62 @@ function escapeDoubleQuotes(string){
return string.replace(/"/g,'\\"');
}
function generateMonitoringDivs(labels, id_prefix){
str = '<pre>'+spinner+'</pre>';
var width = ($(window).width()-129)*40/100;
$.each(labels,function(){
str+='<div class="monitoring_info" id="'+id_prefix+this+'" style="width:'+width+'px; height:150px; margin: 20px 15px; display: inline-block;"></div>';
function generateMonitoringDivs(graphs, id_prefix){
var str = "";
//40% of the width of the screen minus
//129px (left menu size)
var width = ($(window).width()-129)*45/100;
var id_suffix="";
var label="";
$.each(graphs,function(){
label = this.monitor_resources;
id_suffix=label.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;">'+spinner+'</div>\
</td></tr></table>';
});
return str;
}
function plot_graph(data,context,id,label){
function plot_graph(data,context,id_prefix,info){
var labels = info.monitor_resources;
var humanize = info.humanize_figures ? humanize_size : function(val){return val};
var labels_arr = labels.split(',');
var id_suffix = labels.replace(/,/g,'_');
var series = [];
var serie = null;
var serie = {
label : label,
data : data
for (var i = 0; i< labels_arr.length; i++) {
serie = {
label: labels_arr[i],
data: data[i]
};
series.push(serie);
};
var options = {
legend : { show : true },
xaxis : { mode: "time", timeformat: "%h:%M" },
yaxis : { labelWidth: 30 }
legend : { show : true,
noColumns: labels_arr.length,
container: $('#legend_'+id_suffix)
},
xaxis : { mode: "time",
timeformat: "%h:%M"
},
yaxis : { labelWidth: 40,
tickFormatter: function(val, axis) {
return humanize(val);
}
}
}
$.plot($('#'+id, context),[serie],options);
id = id_prefix + id_suffix;
$.plot($('#'+id, context),series,options);
}
//functions that used as true and false conditions for testing mainly

View File

@ -1,7 +1,7 @@
require 'OneMonitor'
class HostMonitor < OneMonitor
#:time label is mandatory and must be first
#:time, :id labels
HOST_MONITORING_ELEMS = {
:time => "LAST_MON_TIME",
:id => "ID",
@ -33,4 +33,12 @@ class HostMonitor < OneMonitor
def snapshot
super HostPool
end
def active (host_hash)
host_hash[:state].to_i < 3
end
def error (host_hash)
host_hash[:state].to_i == 3
end
end

View File

@ -35,7 +35,7 @@ class OneMonitor
def snapshot(poolClass)
#init global results
rc = monitor
rc = monitor #calling the extending class method
rc = save if rc
if rc
@results = []
@ -64,7 +64,11 @@ class OneMonitor
end
@results << hash
add_to_global(hash)
@n_active += 1 if active(hash)
@n_error += 1 if error(hash)
@n_total += 1
end
end
def reinit_global_results
@ -72,6 +76,7 @@ class OneMonitor
@monitoring_elems.each do | key,value |
@global_results[key] = 0
end
@n_active = @n_error = @n_total = 0
end
def add_to_global(hash)

View File

@ -39,11 +39,11 @@ module OneMonitorCSV
if !File.size?(global_log_file)
then
header = csv_header
header = csv_header+",active,error,total"
global_file.puts(header)
end
csv = hash_to_csv(@global_results)
csv = hash_to_csv(@global_results)+%&,"#{@n_active}","#{@n_error}","#{@n_total}"&
global_file.puts(csv)
global_file.close
return 0

View File

@ -7,6 +7,7 @@ class VMMonitor < OneMonitor
:id => "ID",
:name => "NAME",
:lcm_state => "LCM_STATE",
:state => "STATE",
:memory => "MEMORY",
:cpu => "CPU",
:net_tx => "NET_TX",
@ -24,4 +25,12 @@ class VMMonitor < OneMonitor
def snapshot
super VirtualMachinePool
end
def active (vm_hash)
vm_hash[:state].to_i == 3
end
def error (vm_hash)
vm_hash[:state].to_i == 7
end
end

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby
MONITOR_INTERVAL= ARGV[1]? ARGV[1].to_i : 60 #secs
MONITOR_INTERVAL= ARGV[1]? ARGV[1].to_i : 10 #secs
$: << File.dirname(__FILE__)

View File

@ -166,7 +166,7 @@ get '/:resource/monitor' do
end
get '/:resource/:id/monitor' do
@SunstoneServer.get_log(params[:resource],params[:id],settings.config,params['monitor_res'],params['history_length'])
@SunstoneServer.get_log(params[:resource],params[:id],settings.config,params['monitor_resources'],params['history_length'])
end