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

Feature #650: Full graph integration, OneMonitor tool and client with CSV support.

This commit is contained in:
Hector Sanjuan 2011-06-03 13:26:52 +02:00 committed by Ruben S. Montero
parent 3a54ee1ba5
commit 2df1d7bcb2
20 changed files with 926 additions and 291 deletions

View File

@ -23,12 +23,18 @@ if [ -z "$ONE_LOCATION" ]; then
SUNSTONE_LOCK_FILE=/var/lock/one/.sunstone.lock
SUNSTONE_LOG=/var/log/one/sunstone.log
SUNSTONE_CONF=/etc/one/sunstone-server.conf
ONEMONITOR_CMD=/usr/lib/one/sunstone/share/OneMonitor/runOneMonitor.sh
HOST_LOG_FOLDER=/var/log/one/OneMonitor/host
VM_LOG_FOLDER=/var/log/one/OneMonitor/vm
else
SUNSTONE_PID=$ONE_LOCATION/var/sunstone.pid
SUNSTONE_SERVER=$ONE_LOCATION/lib/sunstone/config.ru
SUNSTONE_LOCK_FILE=$ONE_LOCATION/var/.sunstone.lock
SUNSTONE_LOG=$ONE_LOCATION/var/sunstone.log
SUNSTONE_CONF=$ONE_LOCATION/etc/sunstone-server.conf
ONEMONITOR_CMD=$ONE_LOCATION/lib/sunstone/share/OneMonitor/runOneMonitor.sh
HOST_LOG_FOLDER=$ONE_LOCATION/var/OneMonitor/host
VM_LOG_FOLDER=$ONE_LOCATION/var/OneMonitor/vm
fi
setup()
@ -84,6 +90,24 @@ start()
fi
echo "sunstone-server listening on $HOST:$PORT"
# Start the monitoring app
if [ ! -d $HOST_LOG_FOLDER ]
then
mkdir -p $HOST_LOG_FOLDER
[[ $? -ne 0 ]] && ( echo "Error creating host log directory"; exit 1 )
fi
if [ ! -d $VM_LOG_FOLDER ]
then
mkdir -p $VM_LOG_FOLDER
[[ $? -ne 0 ]] && ( echo "Error creating vm log directory"; exit 1 )
fi
$ONEMONITOR_CMD $MONITORING_INTERVAL $HOST_LOG_FOLDER $VM_LOG_FOLDER &>/dev/null &
[[ $? -ne 0 ]] && ( echo "Error launching monitoring daemon"; exit 1 )
}
#
@ -102,6 +126,8 @@ stop()
# Remove pid files
rm -f $SUNSTONE_LOCK_FILE &> /dev/null
killall $ONEMONITOR_CMD
echo "sunstone-server stopped"
}

View File

@ -17,6 +17,8 @@
require 'OpenNebulaJSON'
include OpenNebulaJSON
require 'OneMonitorClient'
class SunstoneServer
def initialize(username, password)
# TBD one_client_user(name) from CloudServer
@ -265,60 +267,22 @@ class SunstoneServer
#
############################################################################
def get_log(resource,id,config,monitor_resources,history_length)
log_file_prefix = case resource
when "vm","VM"
config[:host_log_file]
when "host","HOST"
config[:vm_log_file]
end
def get_log(params)
resource = params[:resource]
id = params[:id]
id = "global" unless id
columns = params['monitor_resources'].split(',')
history_length = params['history_length']
if !log_file_prefix or log_file_prefix.empty?
log_file_prefix = "/srv/cloud/one-dummy/logs/"+resource
end
log_file_folder = case resource
when "vm","VM"
VM_LOG_FOLDER
when "host","HOST"
HOST_LOG_FOLDER
end
log_file = "#{log_file_prefix}_#{id}.csv"
first_line = `head -1 #{log_file}`.chomp
if $?.exitstatus != 0
error = Error.new("Cannot open log file")
return [500, error.to_json]
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
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
monitor_client = OneMonitorClient.new(id,log_file_folder)
return monitor_client.get_data_for_id(id,columns,history_length).to_json
end
############################################################################

View File

@ -55,17 +55,16 @@ table#dashboard_table tr {
vertical-align: top;
}
table#dashboard_table td{
table#dashboard_table > tbody > tr > td{
width:50%;
}
div.panel {
background-color: #ffffff;
padding:0;
width:80%;
margin: 10px;
border: 1px #ddd solid;
min-height: 110px;
min-height: 50px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-moz-box-shadow: 5px 5px 5px #888;
@ -77,6 +76,8 @@ div.panel h3 {
border: 0;
padding:5px 10px 5px 10px;
margin: 0;
color: white;
font-weight: bold;
background-color: #353735;
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
@ -105,7 +106,7 @@ div.panel_info table.info_table tr {
border: 0;
border-bottom: 1px dotted #ccc;
}
div.panel_info table.info_table td {
div.panel_info table.info_table > tbody > tr > td {
border: 0;
width: 100%!important;
}
@ -114,11 +115,11 @@ div.panel_info table.info_table td.value_td {
text-align: right;
}
.key_td_green {
.green {
color: green!important;
}
.key_td_red {
.red {
color: #B81515!important;
}
@ -387,6 +388,12 @@ tr.even:hover{
font-weight:bold;
}
.info_table td.graph_td{
padding-top:0px!important;
padding-bottom:0px!important;
vertical-align:middle!important;
}
.info_table td.value_td{
text-align:left;
}

View File

@ -504,6 +504,37 @@ var OpenNebula = {
}
}
});
},
"monitor_all" : function(params){
var callback = params.success;
var callback_error = params.error;
var resource = OpenNebula.Host.resource;
var data = params.data;
var method = "monitor";
var action = OpenNebula.Helper.action(method);
var request = OpenNebula.Helper.request(resource,method, data);
$.ajax({
url: "host/monitor",
type: "GET",
data: data['monitor'],
dataType: "json",
success: function(response)
{
if (callback)
{
callback(request,response);
}
},
error: function(response)
{
if (callback_error)
{
callback_error(request, OpenNebula.Error(response));
}
}
});
}
},
@ -1369,6 +1400,37 @@ var OpenNebula = {
}
}
});
},
"monitor_all" : function(params){
var callback = params.success;
var callback_error = params.error;
var resource = OpenNebula.VM.resource;
var data = params.data;
var method = "monitor";
var action = OpenNebula.Helper.action(method);
var request = OpenNebula.Helper.request(resource,method, data);
$.ajax({
url: "vm/monitor",
type: "GET",
data: data['monitor'],
dataType: "json",
success: function(response)
{
if (callback)
{
callback(request,response);
}
},
error: function(response)
{
if (callback_error)
{
callback_error(request, OpenNebula.Error(response));
}
}
});
}
},

View File

@ -14,44 +14,81 @@
/* limitations under the License. */
/* -------------------------------------------------------------------------- */
var HISTORY_LENGTH=40;
var GRAPH_AUTOREFRESH_INTERVAL=10000; //10 secs
var graph1 = {
title : "graph1",
monitor_resources : "total,active,error",
history_length : HISTORY_LENGTH
};
var graph2 = {
title : "graph2",
monitor_resources : "cpu_usage,used_cpu,max_cpu",
history_length : HISTORY_LENGTH
};
var graph3 = {
title : "graph3",
monitor_resources : "mem_usage,used_mem,max_mem",
history_length : HISTORY_LENGTH
};
var graph4 = {
title : "graph4",
monitor_resources : "total,active,error",
history_length : HISTORY_LENGTH
};
var graph5 = {
title : "graph5",
monitor_resources : "net_tx,net_rx",
history_length : HISTORY_LENGTH
};
var dashboard_tab_content =
'<table id="dashboard_table">\
<tr>\
<td style="width:40%">\
<table id="information_table">\
<tr>\
<td>\
<div class="panel">\
<h3><a href="#hosts_tab">Hosts</a>\
<div class="new-resource">\
<a class="action_button" href="#hosts_tab" value="Host.create_dialog">+</a>\
</div>\
</h3>\
<div class="panel_info">\
<table class="info_table">\
<tr>\
<td class="key_td">Total</td>\
<td class="value_td"><span id="total_hosts"></span></td>\
</tr>\
<tr>\
<td class="key_td key_td_green">Active</td>\
<td class="value_td"><span id="active_hosts"></span></td>\
</tr>\
</table>\
</div>\
</div>\
</td>\
<td>\
<div class="panel">\
<h3><a href="#hosts_tab">Clusters</a>\
<div class="new-resource">\
<a class="action_button" href="#hosts_tab" value="Cluster.create_dialog">+</a>\
</div>\
</h3>\
<h3>Summary of resources</h3>\
<div class="panel_info">\
<table class="info_table">\
<tr>\
<td class="key_td">Total</td>\
<td class="value_td"><span id="total_clusters"></span></td>\
</tr>\
</table>\
\
<table class="info_table">\
<tr>\
<td class="key_td">Hosts (total/active)</td>\
<td class="value_td"><span id="total_hosts"></span><span id="active_hosts" class="green"></span></td>\
</tr>\
<tr>\
<td class="key_td">Clusters</td>\
<td class="value_td"><span id="total_clusters"></span></td>\
</tr>\
<tr>\
<td class="key_td">VM Templates (total/public)</td>\
<td class="value_td"><span id="total_templates"></span><span id="public_templates"></span></td>\
</tr>\
<tr>\
<td class="key_td">VM Instances (total/<span class="green">running</span>/<span class="red">failed</span>)</td>\
<td class="value_td"><span id="total_vms"></span><span id="running_vms" class="green"></span><span id="failed_vms" class="red"></span></td>\
</tr>\
<tr>\
<td class="key_td">Virtual Networks (total/public)</td>\
<td class="value_td"><span id="total_vnets"></span><span id="public_vnets"></span></td>\
</tr>\
<tr>\
<td class="key_td">Images (total/public)</td>\
<td class="value_td"><span id="total_images"></span><span id="public_images"></span></td>\
</tr>\
<tr>\
<td class="key_td oneadmin">Users</td>\
<td class="value_td oneadmin"><span id="total_users"></span></td>\
</tr>\
</table>\
\
</div>\
</div>\
</td>\
@ -59,95 +96,67 @@ var dashboard_tab_content =
<tr>\
<td>\
<div class="panel">\
<h3><a href="#vms_tab">Virtual Machines</a>\
<div class="new-resource">\
<a class="action_button" href="#vms_tab" value="VM.create_dialog">+</a>\
</div>\
</h3>\
<div class="panel_info">\
<table class="info_table">\
<tr>\
<td class="key_td">Total</td>\
<td class="value_td"><span id="total_vms"></span></td>\
</tr>\
<tr>\
<td class="key_td key_td_green">Running</td>\
<td class="value_td"><span id="running_vms"></span></td>\
</tr>\
<tr>\
<td class="key_td key_td_red">Failed</td>\
<td class="value_td"><span id="failed_vms"></span></td>\
</tr>\
</table>\
</div>\
</div>\
</td>\
<td>\
<div class="panel">\
<h3><a href="#vnets_tab">Virtual Networks</a>\
<div class="new-resource">\
<a class="action_button" href="#vnets_tab" value="Network.create_dialog">+</a>\
</div>\
</h3>\
<div class="panel_info">\
<table class="info_table">\
<tr>\
<td class="key_td">Total</td>\
<td class="value_td"><span id="total_vnets"></span></td>\
</tr>\
<tr>\
<td class="key_td">Public</td>\
<td class="value_td"><span id="public_vnets"></span></td>\
</tr>\
</table>\
</div>\
</div>\
<h3>Quickstart</h3>\
<form id="quickstart_form"><fieldset>\
<table style="width:100%;"><tr style="vertical-align:middle;"><td style="width:70%">\
<label style="font-weight:bold;width:40px;height:7em;">New:</label>\
<input type="radio" name="quickstart" value="Host.create_dialog">Host</input><br />\
<input type="radio" name="quickstart" value="Cluster.create_dialog">Cluster</input><br />\
<input type="radio" name="quickstart" value="Template.create_dialog">VM Template</input><br />\
<input type="radio" name="quickstart" value="VM.create_dialog">VM Instance</input><br />\
<input type="radio" name="quickstart" value="Image.create_dialog">Image</input><br />\
<input type="radio" name="quickstart" value="User.create_dialog">User</input><br />\
</td><td>\
<button id="quickstart">Go</button></fieldset></form>\
</td></tr></table>\
</div>\
</td>\
</tr>\
<tr>\
<td>\
<div class="panel">\
<h3>\
<a href="#images_tab">Images</a>\
<div class="new-resource">\
<a class="action_button" href="#images_tab" value="Image.create_dialog">+</a>\
</div>\
</h3>\
<div class="panel_info">\
<table class="info_table">\
<tr>\
<td class="key_td">Total</td>\
<td class="value_td"><span id="total_images"></span></td>\
</tr>\
<tr>\
<td class="key_td">Public</td>\
<td class="value_td"><span id="public_images"></span></td>\
</tr>\
</table>\
</div>\
<h3>Sunstone documentation</h3>\
<ul style="list-style-type:none;">\
<li>Sunstone installation and setup</li>\
<li>Sunstone plugin guide</li>\
<li>Sunstone plugin reference</li>\
</ul>\
</div>\
</td>\
<td class="oneadmin">\
</tr>\
</table>\
</td>\
<td style="width:60%">\
<table id="historical_table" style="width:100%">\
<tr>\
<td>\
<div class="panel">\
<h3><a href="#users_tab">Users</a>\
<div class="new-resource">\
<a class="action_button" href="#users_tab" value="User.create_dialog">+</a>\
</div>\
</h3>\
<h3>Historical monitoring information</h3>\
<div class="panel_info">\
<table class="info_table">\
<tr>\
<td class="key_td">Total</td>\
<td class="value_td"><span id="total_users"></span></td>\
</tr>\
</table>\
<table class="info_table">\
<tr><td class="key_td graph_td">Total host count</td>\
<td class="graph_td" id="graph1_legend"></td></tr>\
<tr><td id="graph1" colspan="2">'+spinner+'</td></tr>\
<tr><td class="key_td graph_td">Hosts CPU</td>\
<td class="graph_td" id="graph2_legend"></td></tr>\
<tr><td id="graph2" colspan="2">'+spinner+'</td></tr>\
<tr><td class="key_td graph_td">Hosts memory</td>\
<td class="graph_td" id="graph3_legend"></td></tr>\
<tr><td id="graph3" colspan="2">'+spinner+'</td></tr>\
<tr><td class="key_td graph_td">Total VM count</td>\
<td class="graph_td" id="graph4_legend"></td></tr>\
<tr><td id="graph4" colspan="2">'+spinner+'</td></tr>\
<tr><td class="key_td graph_td">VM Network stats</td>\
<td class="graph_td" id="graph5_legend"></td></tr>\
<tr><td id="graph5" colspan="2">'+spinner+'</td></tr>\
</table>\
</div>\
</div>\
</td>\
</tr>\
</table>';
</table>\
</td>\
</tr></table>';
var dashboard_tab = {
title: 'Dashboard',
@ -157,6 +166,74 @@ var dashboard_tab = {
Sunstone.addMainTab('dashboard_tab',dashboard_tab);
function plot_global_graph(data,info){
var id = info.title;
var labels_arr = info.monitor_resources.split(',');
var serie;
var series = [];
var width = ($(window).width()-129)*45/100;
$('#'+id).html('<div id="'+id+'_graph" style="height:70px;width:'+width+'px"><div>');
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,
noColumns: labels_arr.length,
container: $('#'+id+'_legend')},
xaxis : { mode: "time",
timeformat: "%h:%M"
},
yaxis : { labelWidth: 40 }
}
switch (id){
case "graph3":
case "graph5":
options["yaxis"]["tickFormatter"] = function(val,axis) { return humanize_size(val); }
}
$.plot($('#'+id+'_graph'),series,options);
}
function quickstart_setup(){
$('#quickstart').button("disable");
$('#quickstart_form input').click(function(){
$('#quickstart').val($(this).val());
$('#quickstart').button("enable");
});
$('#quickstart').click(function(){
Sunstone.runAction($(this).val());
return false;
});
}
function graph_autorefresh(){
setInterval(function(){
refresh_graphs();
},GRAPH_AUTOREFRESH_INTERVAL);
}
function refresh_graphs(){
Sunstone.runAction("Host.monitor_all", graph1);
Sunstone.runAction("Host.monitor_all", graph2);
Sunstone.runAction("Host.monitor_all", graph3);
Sunstone.runAction("VM.monitor_all", graph4);
Sunstone.runAction("VM.monitor_all", graph5);
}
$(document).ready(function(){
//Dashboard link listener
$("#dashboard_table h3 a").live("click", function (){
@ -164,13 +241,17 @@ $(document).ready(function(){
showTab(tab);
return false;
});
emptyDashboard();
if (uid!=0) {
$("td.oneadmin").hide();
}
quickstart_setup();
refresh_graphs();
graph_autorefresh();
});
//puts the dashboard values into "retrieving"
@ -180,62 +261,73 @@ function emptyDashboard(){
function updateDashboard(what,json_info){
db = $('#dashboard_tab');
switch (what){
case "hosts":
total_hosts=json_info.length;
active_hosts=0;
$.each(json_info,function(){
if (parseInt(this.HOST.STATE) < 3){
active_hosts++;}
});
$('#total_hosts',db).html(total_hosts);
$('#active_hosts',db).html(active_hosts);
break;
case "clusters":
total_clusters=json_info.length;
$('#total_clusters',db).html(total_clusters);
break;
case "vms":
total_vms=json_info.length;
running_vms=0;
var db = $('#dashboard_tab');
switch (what){
case "hosts":
var total_hosts=json_info.length;
var active_hosts=0;
$.each(json_info,function(){
if (parseInt(this.HOST.STATE) < 3){
active_hosts++;}
});
$('#total_hosts',db).html(total_hosts+'&nbsp;/&nbsp;');
$('#active_hosts',db).html(active_hosts);
break;
case "clusters":
var total_clusters=json_info.length;
$('#total_clusters',db).html(total_clusters);
break;
case "vms":
var total_vms=json_info.length;
var running_vms=0;
failed_vms=0;
$.each(json_info,function(){
vm_state = parseInt(this.VM.STATE);
if (vm_state == 3){
running_vms++;
}
else if (vm_state == 7) {
failed_vms++;
}
});
$('#total_vms',db).html(total_vms);
$('#running_vms',db).html(running_vms);
$('#failed_vms',db).html(failed_vms);
break;
case "vnets":
public_vnets=0;
total_vnets=json_info.length;
$.each(json_info,function(){
if (parseInt(this.VNET.PUBLIC)){
public_vnets++;}
});
$('#total_vnets',db).html(total_vnets);
$('#public_vnets',db).html(public_vnets);
break;
case "users":
total_users=json_info.length;
$('#total_users',db).html(total_users);
break;
case "images":
total_images=json_info.length;
public_images=0;
$.each(json_info,function(){
if (parseInt(this.IMAGE.PUBLIC)){
public_images++;}
});
$('#total_images',db).html(total_images);
$('#public_images',db).html(public_images);
break;
}
}
$.each(json_info,function(){
vm_state = parseInt(this.VM.STATE);
if (vm_state == 3){
running_vms++;
}
else if (vm_state == 7) {
failed_vms++;
}
});
$('#total_vms',db).html(total_vms+'&nbsp;/&nbsp;');
$('#running_vms',db).html(running_vms+'&nbsp;/&nbsp;');
$('#failed_vms',db).html(failed_vms);
break;
case "vnets":
var public_vnets=0;
var total_vnets=json_info.length;
$.each(json_info,function(){
if (parseInt(this.VNET.PUBLIC)){
public_vnets++;}
});
$('#total_vnets',db).html(total_vnets+'&nbsp;/&nbsp;');
$('#public_vnets',db).html(public_vnets);
break;
case "users":
var total_users=json_info.length;
$('#total_users',db).html(total_users);
break;
case "images":
var total_images=json_info.length;
var public_images=0;
$.each(json_info,function(){
if (parseInt(this.IMAGE.PUBLIC)){
public_images++;}
});
$('#total_images',db).html(total_images+'&nbsp;/&nbsp;');
$('#public_images',db).html(public_images);
break;
case "templates":
var total_templates=json_info.length;
var public_templates=0;
$.each(json_info,function(){
if (parseInt(this.VMTEMPLATE.PUBLIC)){
public_templates++;
}
});
$('#total_templates',db).html(total_templates+'&nbsp;/&nbsp;');
$('#public_templates',db).html(public_templates);
break;
}
}

View File

@ -231,6 +231,10 @@ var host_actions = {
"Host.monitor_all" : {
type: "monitor_global",
call: OpenNebula.Host.monitor_all,
callback: function(req,response) {
var info = req.request.data[0].monitor;
plot_global_graph(response,info);
},
error: onError
},

View File

@ -663,13 +663,16 @@ function setupCreateImageDialog(){
//Insert HTML in place
$('#create_image_dialog').html(create_image_tmpl);
var height = Math.floor($(window).height()*0.8); //set height to a percentage of the window
//Prepare jquery dialog
$('#create_image_dialog').dialog({
autoOpen: false,
modal:true,
width: 520
});
autoOpen: false,
modal:true,
width: 520,
height: height
});
$('#img_tabs').tabs();
$('#create_image_dialog button').button();
$('#img_type option').first().attr("selected","selected");

View File

@ -803,6 +803,7 @@ function updateTemplateSelect(){
//update static selectors:
$('#create_vm_dialog #template_id').html(templates_select);
$('#speed_virt').html(templates_select);
}
// Callback to update an element in the dataTable

View File

@ -44,9 +44,8 @@ var vm_graphs = [
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!\');">\
@ -364,7 +363,16 @@ var vm_actions = {
'vm_monitor_',info);
},
error: onError
}
},
"VM.monitor_all" : {
type: "monitor_global",
call: OpenNebula.VM.monitor_all,
callback: function(req,response) {
var info = req.request.data[0].monitor;
plot_global_graph(response,info);
},
error: onError
},
}

View File

@ -17,7 +17,7 @@
var cookie = {};
var username = '';
var uid = '';
var spinner = '<img src="/images/ajax-loader.gif" alt="retrieving" class="loading_img"/>';
var spinner = '<img src="/images/ajax-loader.gif" alt="retrieving" class="loading_img" />';
//Sunstone configuration is formed by predifined "actions", main tabs
@ -194,7 +194,7 @@ var Sunstone = {
// * The default actions. Simple call the the pre-defined "call"
// function with an extraparam if defined.
switch (action_cfg.type){
case "create":
case "register":
call({data:data_arg, success: callback, error:err});
@ -206,11 +206,11 @@ var Sunstone = {
call({success: callback, error:err});
break;
case "monitor_global":
call({success: callback, error:err, data: {monitor: extra_param}});
call({timeout: true, success: callback, error:err, data: {monitor: data_arg}});
break;
case "monitor":
case "monitor_single":
call({success: callback, error:err, data: {id:data_arg, monitor: extra_param}});
call({timeout: true, success: callback, error:err, data: {id:data_arg, monitor: extra_param}});
break;
case "multiple":
//run on the list of nodes that come on the data

View File

@ -1,3 +1,19 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'OneMonitor'
class HostMonitor < OneMonitor
@ -22,16 +38,12 @@ class HostMonitor < OneMonitor
:used_cpu => "HOST_SHARE/USED_CPU"
}
def initialize (log_file,monitoring_elems=HOST_MONITORING_ELEMS)
super log_file,monitoring_elems
def initialize (log_file_folder,monitoring_elems=HOST_MONITORING_ELEMS)
super log_file_folder,monitoring_elems
end
def monitor
super HostPool
end
def snapshot
super HostPool
def factory(client)
HostPool.new(client)
end
def active (host_hash)

View File

@ -1,3 +1,19 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
ONE_LOCATION = ENV["ONE_LOCATION"]
if !ONE_LOCATION
@ -19,10 +35,10 @@ class OneMonitor
when "CSV" then include OneMonitorCSV
end
def initialize(log_file_prefix,monitoring_elems)
def initialize(log_file_folder,monitoring_elems)
# Authenticate in OpenNebula
@client = Client.new
@log_file_prefix = log_file_prefix
@log_file_folder = log_file_folder
@monitoring_elems = monitoring_elems
@results = []
reinit_global_results
@ -32,7 +48,7 @@ class OneMonitor
@results
end
def snapshot(poolClass)
def snapshot
#init global results
rc = monitor #calling the extending class method
@ -47,23 +63,31 @@ class OneMonitor
return rc
end
def monitor(poolClass)
pool = poolClass.new(@client)
def monitor
pool = factory(@client)
rc = pool.info
if OpenNebula.is_error?(rc)
then
puts "Error monitoring: #{rc}"
puts "Error monitoring: #{rc.message}"
return nil
end
pool.each do | elem |
time = elem[@monitoring_elems[:time]].to_i
hash = {}
@monitoring_elems.each do | key,value |
hash[key] = elem[value]
end
@results << hash
add_to_global(hash)
#do not log time = 0, it causes
#graphs being drawn from 1970
if time > 0
@results << hash
add_to_global(hash)
end
@n_active += 1 if active(hash)
@n_error += 1 if error(hash)
@n_total += 1
@ -83,7 +107,8 @@ class OneMonitor
hash.each do | key,value |
@global_results[key] += value.to_i
end
@global_results[:time] = hash[:time].to_i
time = hash[:time].to_i
@global_results[:time] = time
end
end

View File

@ -0,0 +1,35 @@
require 'OneMonitorClientUtils'
class OneMonitorClient
INPUT_METHOD="CSV"
case INPUT_METHOD
when "CSV" then include OneMonitorCSVClient
end
def initialize(ids, log_file_folder)
#create filenames to read
ids = [ids] unless ids.class == Array
@file_names = {}
ids.each do | id |
@file_names[id] = OneMonitorClient.full_path(log_file_folder,id)
end
return @file_names
end
def get_multiple_data(columns,length)
result = []
@file_names.each do | id,file_name |
result << get_data_for_id(id,columns,length)
end
return result
end
def get_data_for_id(id, columns, length)
readOneMonitorFile(@file_names[id],columns,length)
end
def self.full_path(folder,id)
"#{folder}/#{id}"
end
end

View File

@ -0,0 +1,40 @@
module OneMonitorCSVClient
def readOneMonitorFile(file_name,columns,length)
first_line = `head -1 #{file_name}`.chomp
if $?.exitstatus != 0
return [] #silently fail, cannot find this file
end
n_lines = `wc -l #{file_name} | cut -d' ' -f 1`.to_i
if n_lines <= length.to_i
length = n_lines-1
end
fields = first_line.split(',')
poll_time_pos = fields.index("time")
if !poll_time_pos
return [] #silently fail, no timestamp
end
tail = `tail -#{length} #{file_name}`
series = [] #will hold several graphs
columns.each do | column_name |
graph = []
column_pos = fields.index(column_name)
next unless column_pos
tail.each_line do | line |
line_arr = line.delete('"').split(',')
graph << [ line_arr[poll_time_pos].to_i*1000, line_arr[column_pos].to_i ]
end
series << graph
end
return series
end
end

View File

@ -1,3 +1,19 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
module OneMonitorCSV
def save
@ -9,13 +25,12 @@ module OneMonitorCSV
@results.each do | mon_hash |
id = mon_hash[:id]
log_name = "#{@log_file_prefix}_#{id}.csv"
log_name = "#{@log_file_folder}/#{id}"
begin
log_file = File.new(log_name,'a')
if !File.size?(log_name)
then
header = csv_header
log_file.puts(header)
end
@ -34,7 +49,7 @@ module OneMonitorCSV
def save_global_csv
begin
global_log_file = "#{@log_file_prefix}_global.csv"
global_log_file = "#{@log_file_folder}/global"
global_file = File.new(global_log_file,'a')
if !File.size?(global_log_file)

View File

@ -1,3 +1,19 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'OneMonitor'
class VMMonitor < OneMonitor
@ -14,16 +30,12 @@ class VMMonitor < OneMonitor
:net_rx => "NET_RX"
}
def initialize (log_file,monitoring_elems=VM_MONITORING_ELEMS)
super log_file,monitoring_elems
def initialize (log_file_folder,monitoring_elems=VM_MONITORING_ELEMS)
super log_file_folder,monitoring_elems
end
def monitor
super VirtualMachinePool
end
def snapshot
super VirtualMachinePool
def factory(client)
VirtualMachinePool.new(client)
end
def active (vm_hash)

View File

@ -0,0 +1,41 @@
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
$: << File.dirname(__FILE__)
require 'HostMonitor.rb'
require 'VMMonitor.rb'
DEFAULT_INTERVAL= 600 #secs
DEFAULT_HOST_LOG_FOLDER = "#{ENV['ONE_LOCATION']}/logs/host/"
DEFAULT_VM_LOG_FOLDER = "#{ENV['ONE_LOCATION']}/logs/vm/"
#ARG0=interval, ARG1=hostfolder, ARG2=vmfolder
MONITOR_INTERVAL= ARGV[0]? ARGV[0].to_i : DEFAULT_INTERVAL #secs
HOST_LOG_FOLDER= ARGV[1]? ARGV[1]: DEFAULT_HOST_LOG_FOLDER
VM_LOG_FOLDER=ARGV[2] ? ARGV[2] : DEFAULT_VM_LOG_FOLDER
hostm = HostMonitor.new(HOST_LOG_FOLDER)
vmm = VMMonitor.new(VM_LOG_FOLDER)
while true do
hostm.snapshot
vmm.snapshot
sleep MONITOR_INTERVAL
end

View File

@ -1,20 +0,0 @@
#!/usr/bin/env ruby
MONITOR_INTERVAL= ARGV[1]? ARGV[1].to_i : 10 #secs
$: << File.dirname(__FILE__)
require 'HostMonitor.rb'
require 'VMMonitor.rb'
FILE= ARGV[0]? ARGV[0]: "#{ENV['ONE_LOCATION']}/logs/"
hostm = HostMonitor.new(FILE+"host")
vmm = VMMonitor.new(FILE+"vm")
while true do
hostm.snapshot
vmm.snapshot
sleep MONITOR_INTERVAL
end

View File

@ -31,8 +31,12 @@ else
CONFIGURATION_FILE = ONE_LOCATION+"/etc/sunstone-server.conf"
end
HOST_LOG_FOLDER = LOG_LOCATION+"/OneMonitor/host"
VM_LOG_FOLDER = LOG_LOCATION+"/OneMonitor/vm"
$: << RUBY_LIB_LOCATION
$: << File.dirname(__FILE__)+'/models'
$: << File.dirname(__FILE__)+'/share/OneMonitor'
##############################################################################
# Required libraries
@ -91,6 +95,7 @@ helpers do
session.clear
return [204, ""]
end
end
before do
@ -161,12 +166,11 @@ end
##############################################################################
get '/:resource/monitor' do
@SunstoneServer.get_log(params)
end
get '/:resource/:id/monitor' do
@SunstoneServer.get_log(params[:resource],params[:id],settings.config,params['monitor_resources'],params['history_length'])
@SunstoneServer.get_log(params)
end

View File

@ -0,0 +1,304 @@
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
ONE_LOCATION = ENV["ONE_LOCATION"]
if !ONE_LOCATION
LOG_LOCATION = "/var/log/one"
VAR_LOCATION = "/var/lib/one"
RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
CONFIGURATION_FILE = "/etc/one/sunstone-server.conf"
else
VAR_LOCATION = ONE_LOCATION+"/var"
LOG_LOCATION = ONE_LOCATION+"/var"
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
CONFIGURATION_FILE = ONE_LOCATION+"/etc/sunstone-server.conf"
end
HOST_LOG_FOLDER = LOG_LOCATION+"/OneMonitor/host"
VM_LOG_FOLDER = LOG_LOCATION+"/OneMonitor/vm"
#TODO put share
MONITOR_CMD = File.dirname(__FILE__)+'/share/OneMonitor/runOneMonitor.rb'
MONITORING_INTERVAL = 10 #seconds
$: << RUBY_LIB_LOCATION
$: << File.dirname(__FILE__)+'/models'
##############################################################################
# Required libraries
##############################################################################
require 'rubygems'
require 'sinatra'
require 'cloud/Configuration'
require 'SunstoneServer'
set :config, Configuration.new(CONFIGURATION_FILE)
##############################################################################
# Sinatra Configuration
##############################################################################
use Rack::Session::Pool
set :host, settings.config[:host]
set :port, settings.config[:port]
##############################################################################
# Helpers
##############################################################################
helpers do
def authorized?
session[:ip] && session[:ip]==request.ip ? true : false
end
def build_session
auth = Rack::Auth::Basic::Request.new(request.env)
if auth.provided? && auth.basic? && auth.credentials
user = auth.credentials[0]
sha1_pass = Digest::SHA1.hexdigest(auth.credentials[1])
rc = SunstoneServer.authorize(user, sha1_pass)
if rc[1]
session[:user] = user
session[:user_id] = rc[1]
session[:password] = sha1_pass
session[:ip] = request.ip
session[:remember] = params[:remember]
if params[:remember]
env['rack.session.options'][:expire_after] = 30*60*60*24
end
return [204, ""]
else
return [rc.first, ""]
end
end
return [401, ""]
end
def destroy_session
session.clear
return [204, ""]
end
def start_monitor
if !session['monitor']
config = settings.config
begin
Dir.mkdir(HOST_LOG_FOLDER) if !File.directory?(HOST_LOG_FOLDER)
Dir.mkdir(VM_LOG_FOLDER) if !File.directory?(VM_LOG_FOLDER)
rescue
return [500,Error.new("Cannot create log folders")]
end
monitoring_interval = config[:monitoring_interval]
monitoring_interval = MONITORING_INTERVAL if !monitoring_interval
pipe = IO.popen("#{MONITOR_CMD} #{monitoring_interval} #{HOST_LOG_FOLDER} #{VM_LOG_FOLDER}")
session['monitor'] = pipe
puts "OneMonitor successfully started"
return [200,""]
end
end
def stop_monitor
pipe = session['monitor']
if pipe
Process.kill('KILL',pipe.pid)
pipe.close
end
return [200,""]
end
end
before do
unless request.path=='/login' || request.path=='/'
halt 401 unless authorized?
@SunstoneServer = SunstoneServer.new(session[:user], session[:password])
end
end
after do
unless request.path=='/login' || request.path=='/'
unless session[:remember]
if params[:timeout] == true
env['rack.session.options'][:defer] = true
else
env['rack.session.options'][:expire_after] = 60*10
end
end
end
end
##############################################################################
# HTML Requests
##############################################################################
get '/' do
redirect '/login' unless authorized?
time = Time.now + 60
response.set_cookie("one-user",
:value=>"#{session[:user]}",
:expires=>time)
response.set_cookie("one-user_id",
:value=>"#{session[:user_id]}",
:expires=>time)
File.read(File.dirname(__FILE__)+'/templates/index.html')
end
get '/login' do
File.read(File.dirname(__FILE__)+'/templates/login.html')
end
##############################################################################
# Login
##############################################################################
post '/login' do
build_session
end
post '/logout' do
destroy_session
end
##############################################################################
# Config and Logs
##############################################################################
get '/config' do
@SunstoneServer.get_configuration(session[:user_id])
end
get '/vm/:id/log' do
@SunstoneServer.get_vm_log(params[:id])
end
##############################################################################
# Logs
##############################################################################
post '/startmonitor' do
start_monitor
end
get '/:resource/monitor' do
@SunstoneServer.get_log(params[:resource],
-1,
settings.config,
params['monitor_resources'],
params['history_length'])
end
get '/:resource/:id/monitor' do
@SunstoneServer.get_log(params[:resource],
params[:id],
settings.config,
params['monitor_resources'],
params['history_length'])
end
##############################################################################
# GET Pool information
##############################################################################
get '/:pool' do
@SunstoneServer.get_pool(params[:pool])
end
##############################################################################
# GET Resource information
##############################################################################
get '/:resource/:id' do
@SunstoneServer.get_resource(params[:resource], params[:id])
end
##############################################################################
# Delete Resource
##############################################################################
delete '/:resource/:id' do
@SunstoneServer.delete_resource(params[:resource], params[:id])
end
##############################################################################
# Create a new Resource
##############################################################################
post '/:pool' do
@SunstoneServer.create_resource(params[:pool], request.body.read)
end
##############################################################################
# Stop the VNC Session of a target VM
##############################################################################
post '/vm/:id/stopvnc' do
vm_id = params[:id]
vnc_hash = session['vnc']
if !vnc_hash || !vnc_hash[vm_id]
msg = "It seems there is no VNC proxy running for this machine"
return [403, OpenNebula::Error.new(msg).to_json]
end
rc = @SunstoneServer.stopvnc(vm_id, vnc_hash[vm_id][:pipe])
if rc[0] == 200
session['vnc'].delete(vm_id)
end
rc
end
##############################################################################
# Start a VNC Session for a target VM
##############################################################################
post '/vm/:id/startvnc' do
vm_id = params[:id]
vnc_hash = session['vnc']
if !vnc_hash
session['vnc']= {}
elsif vnc_hash[vm_id]
#return existing information
info = vnc_hash[vm_id].clone
info.delete(:pipe)
return [200, info.to_json]
end
rc = @SunstoneServer.startvnc(vm_id, settings.config)
if rc[0] == 200
info = rc[1]
session['vnc'][vm_id] = info.clone
info.delete(:pipe)
[200, info.to_json]
else
rc
end
end
##############################################################################
# Perform an action on a Resource
##############################################################################
post '/:resource/:id/action' do
@SunstoneServer.perform_action(params[:resource], params[:id], request.body.read)
end