From f79fde6d3b3a9704249319e615c4d2acea5c2ef8 Mon Sep 17 00:00:00 2001 From: xq2005 Date: Tue, 9 Aug 2011 09:10:51 +0000 Subject: [PATCH] integrate ganglia into xcat gui git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@10247 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd --- xCAT-UI/css/style.css | 9 +- xCAT-UI/ganglianode.php | 83 +++ xCAT-UI/js/monitor/gangliamon.js | 893 +++++++++++++++++++--------- xCAT-server/lib/xcat/plugins/web.pm | 288 ++++++++- 4 files changed, 986 insertions(+), 287 deletions(-) create mode 100644 xCAT-UI/ganglianode.php diff --git a/xCAT-UI/css/style.css b/xCAT-UI/css/style.css index 9cd1e506f..a9d28c8c2 100644 --- a/xCAT-UI/css/style.css +++ b/xCAT-UI/css/style.css @@ -713,11 +713,16 @@ table a:hover { .monitorsumdiv { width: 300px; - height: 150px; + height: 180px; float: left; - margin: 0px 0px 10px 0px; + margin: 0px 0px 10px 10px; } +.monitorsumdiv td{ + padding: 0; + border-style: none; + font-size: 10px; +} .monitornodediv { width: 240px; height: 120px; diff --git a/xCAT-UI/ganglianode.php b/xCAT-UI/ganglianode.php new file mode 100644 index 000000000..f9907ca82 --- /dev/null +++ b/xCAT-UI/ganglianode.php @@ -0,0 +1,83 @@ + + + Node {$_GET['n']} Ganglia Report + + + + + + + +EEE; +?> + + + + + +
+ + + + + + +
+
+ + +EEE; +?> \ No newline at end of file diff --git a/xCAT-UI/js/monitor/gangliamon.js b/xCAT-UI/js/monitor/gangliamon.js index 1aeec021b..69795cdfd 100644 --- a/xCAT-UI/js/monitor/gangliamon.js +++ b/xCAT-UI/js/monitor/gangliamon.js @@ -1,8 +1,19 @@ /** * Global variables */ -var gangliaTableId = 'nodesDatatable'; -var gangliaData; +//save grid summary data, update every one minute +var gridData; + +//save nodes path, used for getting detail from rrd file +var nodePath = new Object(); + +//save nodes current status, +//unknown-> -2, error->-1, warning->0 ,normal->1 which is used for sorting +var nodeStatus = new Object(); + +//update timer +var gangliaTimer; + /** * Load Ganglia monitoring tool @@ -11,61 +22,33 @@ var gangliaData; */ function loadGangliaMon() { // Get Ganglia tab - var gangliaTab = $('#gangliamon'); + $('#gangliamon').append(createInfoBar('Checking RPMs.')); + + //should get the groups first + if (!$.cookie('groups')){ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'extnoderange', + tgt : '/.*', + args : 'subgroups', + msg : '' + }, + success : setGroupsCookies + }); + } // Check whether Ganglia RPMs are installed on the xCAT MN $.ajax({ url : 'lib/systemcmd.php', dataType : 'json', data : { - cmd : 'rpm -q rrdtool ganglia-gmetad ganglia-gmond ganglia-web' + cmd : 'rpm -q rrdtool ganglia-gmetad ganglia-gmond' }, success : checkGangliaRPMs }); - - // Create groups and nodes DIV - var groups = $('
'); - var nodes = $('
'); - gangliaTab.append(groups); - gangliaTab.append(nodes); - - // Create info bar - var gangliaLnk = $('Click here'); - gangliaLnk.css( { - 'color' : 'blue', - 'text-decoration' : 'none' - }); - gangliaLnk.click(function() { - // Open a new window for Ganglia - window.open('../ganglia/'); - }); - - // Create info bar - var info = $('
'); - info.append(''); - var msg = $('

'); - msg.append('Select a group to view the nodes summary. '); - msg.append(gangliaLnk); - msg.append(' to open the Ganglia page.'); - info.append(msg); - info.css('margin-bottom', '10px'); - nodes.append(info); - - // Get groups - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'extnoderange', - tgt : '/.*', - args : 'subgroups', - msg : '' - }, - - success : loadGroups4Ganglia - }); - return; } @@ -78,10 +61,10 @@ function loadGangliaMon() { */ function checkGangliaRPMs(data) { var gangliaTab = $('#gangliamon'); - + gangliaTab.empty(); // Get the list of Ganglia RPMs installed var status = data.rsp.split(/\n/); - var gangliaRPMs = [ "rrdtool", "ganglia-gmetad", "ganglia-gmond", "ganglia-web" ]; + var gangliaRPMs = [ "rrdtool", "ganglia-gmetad", "ganglia-gmond"]; var warningMsg = 'Before continuing, please install the following packages: '; var missingRPMs = false; for ( var i in status) { @@ -99,6 +82,7 @@ function checkGangliaRPMs(data) { warningBar.css('margin-bottom', '10px'); warningBar.prependTo(gangliaTab); } else { + gangliaTab.append(createInfoBar('Checking Running status.')); // Check if ganglia is running on the xCAT MN $.ajax( { url : 'lib/cmd.php', @@ -110,255 +94,598 @@ function checkGangliaRPMs(data) { msg : '' }, - /** - * Append warning message - * - * @param data - * Data returned from HTTP request - * @return Nothing - */ - success : function(data) { - if (data.rsp[0].indexOf("not-monitored") > -1) { - // Create link to start Ganglia - var startLnk = $('Click here'); - startLnk.css( { - 'color' : 'blue', - 'text-decoration' : 'none' - }); - startLnk.click(function() { - // Turn on Ganglia for all nodes - monitorNode('', 'on'); - }); - - // Create warning bar - var warningBar = $('
'); - var msg = $('

'); - msg.append(''); - msg.append('Please start Ganglia Monitoring on xCAT. '); - msg.append(startLnk); - msg.append(' to start Ganglia Monitoring.'); - warningBar.append(msg); - warningBar.css('margin-bottom', '10px'); - - // If there are any warning messages, append this warning after it - var curWarnings = $('#gangliamon').find('.ui-state-error'); - var gangliaTab = $('#gangliamon'); - if (curWarnings.length) { - curWarnings.after(warningBar); - } else { - warningBar.prependTo(gangliaTab); - } - } - } + success : checkGangliaRunning }); } + return; } /** - * Load groups - * - * @param data - * Data returned from HTTP request - * @return - */ -function loadGroups4Ganglia(data) { - // Remove loader - $('#groups').find('img').remove(); - - // Save group in cookie - var groups = data.rsp; - setGroupsCookies(data); - - // Create a list of groups - $('#groups').append('
Groups
'); - var grouplist= $('
'); - // Create a link for each group - for (var i = groups.length; i--;) { - grouplist.append('
' + groups[i] + '
'); - } - - $('#groups').append(grouplist); - - // Bind the click event - $('#groups .groupdiv div').bind('click', function(){ - $('#nodes .jqplot-target').remove(); - - // Create loader - var loader = createLoader(); - loader.css('padding', '5px'); - $('#nodes').append(loader); - - var thisGroup = $(this).text(); - $('#groups .groupdiv div').removeClass('selectgroup'); - $(this).addClass('selectgroup'); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodels', - tgt : thisGroup, - args : '', - msg : thisGroup - }, - - /** - * Get node definitions - * - * @param data - * Data returned from HTTP request - * @return Nothing - */ - success : function(data) { - var group = data.msg; - - // Get nodes definitions - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodestat', - tgt : group, - args : '', - msg : group - }, - - success : loadGangliaSummary - }); - } - }); - }); -} - -/** - * Load Ganglia summary page + * Check whether Ganglia is running * * @param data * Data returned from HTTP request * @return Nothing */ -function loadGangliaSummary(data) { - // Data returned - var rsp = data.rsp; - // Group name - var group = data.msg; - // Node attributes hash - var attrs = new Object(); - - var node, status, args; - for ( var i in rsp) { - // Get key and value - args = rsp[i].split(':', 2); - node = jQuery.trim(args[0]); - status = jQuery.trim(args[1]); - - // Create a hash table - attrs[node] = new Object(); - attrs[node]['status'] = status; - } - - // Save node attributes hash - gangliaData = attrs; - - // Get the status of Ganglia - // Then create pie chart for node and Ganglia status - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastatus;' + group, - msg : '' - }, +function checkGangliaRunning(data){ + var gangliaTab = $('#gangliamon'); + var groupsSelectStr = ''; + var groupsArray = $.cookie('groups').split(','); + gangliaTab.empty(); + if (data.rsp[0].indexOf("not-monitored") > -1) { + // Create link to start Ganglia + var startLnk = $('Click here'); + startLnk.css( { + 'color' : 'blue', + 'text-decoration' : 'none' + }); + startLnk.click(function() { + // Turn on Ganglia for all nodes + monitorNode('', 'on'); + }); - success : loadGangliaStatus - }); -} + // Create warning bar + var warningBar = $('
'); + var msg = $('

'); + msg.append(''); + msg.append('Please start Ganglia Monitoring on xCAT. '); + msg.append(startLnk); + msg.append(' to start Ganglia Monitoring.'); + warningBar.append(msg); + warningBar.css('margin-bottom', '10px'); -/** - * Load the status of Ganglia for a given group - * - * @param data - * Data returned from HTTP request - * @return Nothing - */ -function loadGangliaStatus(data) { - // Remove loader - $('#nodes').find('img').remove(); - - // Get datatable - var ganglia = data.rsp; - var node, ping, monitored; + // If there are any warning messages, append this warning after it + var curWarnings = $('#gangliamon').find('.ui-state-error'); + + if (curWarnings.length) { + curWarnings.after(warningBar); + } else { + warningBar.prependTo(gangliaTab); + } + + return; + } - // Count nodes that are pingable and not pingable - // and nodes that are pingable and monitored by Ganglia - var pingWGanglia = 0; - var pingWOGanglia = 0; - var noping = 0; - for ( var i in ganglia) { - // ganglia[0] = nodeName and ganglia[1] = state - node = jQuery.trim(ganglia[i][0]); - if (node) { - monitored = jQuery.trim(ganglia[i][1]); - ping = gangliaData[node]['status']; - - // If the node is monitored, increment count - if (ping == 'sshd' && monitored == 'on') { - pingWGanglia++; - } else if (ping == 'sshd' && monitored == 'off') { - pingWOGanglia++; - } else { - noping++; - } - } - } - - // Create pie chart - var summary = $('
'); - $('#nodes').append(summary); - - // Create pie details - var details = $('
'); - $('#nodes').append(details); - - var pie = [['Ping + monitored', pingWGanglia], ['Ping + not monitored', pingWOGanglia], ['Noping', noping]]; - var plot = $.jqplot('ganglia_sum', - [pie], { - seriesDefaults: { - renderer: $.jqplot.PieRenderer, - rendererOptions: { - padding: 5, - fill:true, - shadow:true, - shadowOffset: 2, - shadowDepth: 5, - shadowAlpha: 0.07, - dataLabels : 'value', - showDataLabels: true - } - }, - legend: { - show: true, - location: 'e' + groupsSelectStr = ''; + + //help info + var helpStr = '
aaa
'; + + //pass checking + var showStr = '

Grid Overview


' + + '
' + + '

Nodes Current Status

' + + ' ?' + + '
Nodes in Group:' + groupsSelectStr + + ' order by: ' + + '
'; + + //ganglia help information + + gangliaTab.append(showStr); + + //get summary data and draw on the page + $('#gangliaGridSummary').append('Getting Grid summary Data.'); + sendGridSummaryAjax(); + + //get nodes current status and draw on the page + $('#gangliaNodes').append('Getting ' + $('#gangliagroup').val() + ' nodes Status.'); + sendNodeCurrentAjax(); + + //start the timer to update page per minute. + gangliaTimer = window.setTimeout('updateGangliaPage()', 60000); + + //bind the group select change event + $('#gangliagroup').bind('change', function(){ + var groupname = $(this).val(); + $('#gangliaNodes').html('Getting ' + groupname + ' nodes Status.'); + sendNodeCurrentAjax(); + }); + + //bind the order select change event + $('#gangliaorder').bind('change', function(){ + drawGangliaNodesArea($(this).val()); + }); + + //bind the info click enent + $('#gangliamon sup').bind('click', function(){ + var helpStr = '' + + '' + + '' + + '' + + '' + + '
Normal
Heavy Load
Can not get status longer than 3 minutes
Unknown
'; + var helpDia = $('
'); + helpDia.append(helpStr); + helpDia.dialog({ + modal: true, + width: 350, + title: 'Node Status Help Info', + close: function(){$(this).remove();}, + buttons: { + 'Close': function(){ + $(this).dialog('close'); + } } }); - - // Change CSS styling for legend - summary.find('table').css({ - 'border-style': 'none' - }).find('td').css({ - 'border-style': 'none' - }); + }); +} - // Open nodes page on-click - $('#ganglia_sum').bind('jqplotDataClick', function(env, srIndex, ptIndex, data) { - window.open('../xcat/index.php'); - }); - - // Special note - // To redraw pie chart: - // - Use chart.series[0].data[i] to reference existing data - // - Use chart.redraw() to redraw chart +/** + * send ajax request to get grid summary information + * + * @param + * + * @return Nothing + */ +function sendGridSummaryAjax(){ + //get the summary data + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliashow;_grid_;hour;_summary_', + msg : '' + }, + + success: function(data){ + createGridSummaryData(data.rsp[0]); + drawGridSummary(); + } + }); +} + +/** + * send ajax request to get nodes current load information + * + * @param which group name want to get + * + * @return Nothing + */ +function sendNodeCurrentAjax(){ + var groupname = $('#gangliagroup').val(); + //get all nodes current status + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliacurrent;node;' + groupname, + msg : '' + }, + + success: function(data){ + createNodeStatusData(data.rsp[0]); + drawGangliaNodesArea($('#gangliaorder').val()); + } + }); +} + +/** + * send ajax request to get grid current summary information for update the page + * + * @param + * + * @return Nothing + */ +function sendGridCurrentAjax(){ + //get the summary data + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliacurrent;grid', + msg : '' + }, + + success: function(data){ + updateGridSummaryData(data.rsp[0]); + drawGridSummary(); + } + }); +} + +/** + * save the grid summary data to local global variable + * + * @param data structure + * metric1:time11,val11,time12,val12....;metric2:time21,val21,time22,val22,...;.... + * @return Nothing + */ +function createGridSummaryData(summaryString){ + //empty the global data + gridData = new Object(); + + var metricArray = summaryString.split(';'); + var metricname = ''; + var valueArray = ''; + var position = 0; + var tempLength = 0; + for (var index = 0; index < metricArray.length; index++){ + position = metricArray[index].indexOf(':'); + //get the metric name and init its global array to save timestamp and value pair + metricname = metricArray[index].substr(0, position); + gridData[metricname] = new Array(); + valueArray = metricArray[index].substr(position + 1).split(','); + tempLength = valueArray.length; + //save timestamp and value into global array + for (var i = 0; i < tempLength; i++){ + gridData[metricname].push(Number(valueArray[i])); + } + } +} + +/** + * update the grid summary data to local global variable + * + * @param data structure + * metric1:time11,val11;metric2:time21,val21,time22;.... + * @return Nothing + */ +function updateGridSummaryData(currentString){ + var metricArray = currentString.split(';'); + var metricname = ''; + var position = 0; + var tempLength = 0; + var index = 0; + var tempArray; + + tempLength = metricArray.length; + for (index = 0; index < tempLength; index++){ + position = metricArray[index].indexOf(':'); + metricname = metricArray[index].substr(0, position); + tempArray = metricArray[index].substr(position + 1).split(','); + if (gridData[metricname]){ + gridData[metricname].shift(); + gridData[metricname].shift(); + gridData[metricname].push(Number(tempArray[0])); + gridData[metricname].push(Number(tempArray[1])); + } + } +} +/** + * draw the Grid summay area by global data + * + * @param data + * Data returned from HTTP request + * @return Nothing + */ +function drawGridSummary() { + var gridDrawArea = $('#gangliaGridSummary'); + var showStr = ''; + var tempStr = $('#gangliamon').attr('class'); + + //jqflot only draw on the area visiable, if the tab is hide, return directly + if (-1 != tempStr.indexOf('hide')){ + return; + }; + gridDrawArea.empty(); + showStr = '' + + '' + + '
'; + gridDrawArea.append(showStr); + drawLoadFlot('gangliasummaryload', 'Grid', gridData['load_one'], gridData['cpu_num']); + drawCpuFlot('gangliasummarycpu', 'Grid', gridData['cpu_idle']); + drawMemFlot('gangliasummarymem', 'Grid', gridData['mem_free'], gridData['mem_total']); +} + +/** + * draw the load flot by data(maybe summary data, or one node's data) + * + * @param areaid: which div draw this flot + * loadpair: the load timestamp and value pair + * cpupair: the cpu number and value pair + * + * @return Nothing + */ +function drawLoadFlot(areaid, titleprefix, loadpair, cpupair){ + var load = new Array(); + var cpunum = new Array(); + var index = 0; + var templength = 0; + var yaxismax = 0; + var interval = 1; + + $('#' + areaid).empty(); + //parse load pair, the timestamp must mutiply 1000, javascript time stamp is millisecond + templength = loadpair.length; + for (index = 0; index < templength; index += 2){ + load.push([loadpair[index] * 1000, loadpair[index + 1]]); + if (loadpair[index + 1] > yaxismax){ + yaxismax = loadpair[index + 1]; + } + } + + //parse cpu pair + templength = cpupair.length; + for (index = 0; index < templength; index += 2){ + cpunum.push([cpupair[index] * 1000, cpupair[index + 1]]); + if (cpupair[index + 1] > yaxismax){ + yaxismax = cpupair[index + 1]; + } + } + + interval = parseInt(yaxismax / 3); + if (interval < 1){ + interval = 1; + } + $.jqplot(areaid, [load, cpunum],{ + title: titleprefix + ' Loads/Procs Last Hour', + axes:{ + xaxis:{ + renderer : $.jqplot.DateAxisRenderer, + numberTicks: 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis: { + min : 0, + tickInterval : interval + } + }, + legend : { + show: true, + location: 'nw' + }, + series:[{label:'Load'}, {label: 'CPU Number'}], + seriesDefaults : {showMarker: false} + } + ); +} + +/** + * draw the cpu usage flot by data(maybe summary data, or one node's data) + * + * @param areaid: which div draw this flot + * titleprefix : title used name + * cpupair: the cpu timestamp and value pair + * + * @return Nothing + */ +function drawCpuFlot(areaid, titleprefix, cpupair){ + var cpu = new Array(); + var index = 0; + var tempLength = 0; + + $('#' + areaid).empty(); + tempLength = cpupair.length; + // time stamp should mutiply 1000 + // we get the cpu idle from server, we should use 1 subtract the idle. + for(index = 0; index < tempLength; index +=2){ + cpu.push([(cpupair[index] * 1000), (100 - cpupair[index + 1])]); + } + + $.jqplot(areaid, [cpu],{ + title: titleprefix + ' Cpu Use Last Hour', + axes:{ + xaxis:{ + renderer : $.jqplot.DateAxisRenderer, + numberTicks: 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis: { + min : 0, + max : 100, + tickOptions:{formatString : '%d\%'} + } + }, + seriesDefaults : {showMarker: false} + } + ); +} + +/** + * draw the memory usage flot by data(maybe summary data, or one node's data) + * + * @param areaid: which div draw this flot + * titleprefix : title used name + * cpupair: the cpu timestamp and value pair + * + * @return Nothing + */ +function drawMemFlot(areaid, titleprefix, freepair, totalpair){ + var use = new Array(); + var total = new Array(); + var tempsize = 0; + var index = 0; + + $('#' + areaid).empty(); + if(freepair.length < totalpair.length){ + tempsize = freepair.length; + } + else{ + tempsize = freepair.length; + } + + for(index = 0; index < tempsize; index += 2){ + var temptotal = totalpair[index + 1]; + var tempuse = temptotal - freepair[index + 1]; + temptotal = temptotal / 1000000; + tempuse = tempuse / 1000000; + total.push([totalpair[index] * 1000, temptotal]); + use.push([freepair[index] * 1000, tempuse]); + } + + $.jqplot(areaid, [use, total],{ + title: titleprefix + ' Memory Use Last Hour', + axes:{ + xaxis:{ + renderer : $.jqplot.DateAxisRenderer, + numberTicks: 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis: { + min : 0, + tickOptions:{formatString : '%.2fG'} + } + }, + legend : { + show: true, + location: 'nw' + }, + series:[{label:'Used'}, {label: 'Total'}], + seriesDefaults : {showMarker: false} + } + ); +} + +function createNodeStatusData(nodesStatus){ + var index; + var nodesArray = nodesStatus.split(';'); + var position = 0; + var nodename = ''; + var index = 0; + var tempArray; + var tempStr = ''; + var templength = nodesArray.length; + + for (index in nodePath){ + delete(nodePath[index]); + } + + for (index in nodeStatus){ + delete(nodeStatus[index]); + } + + for (index = 0; index < templength; index++){ + tempStr = nodesArray[index]; + position = tempStr.indexOf(':'); + nodename = tempStr.substring(0, position); + tempArray = tempStr.substring(position + 1).split(','); + + switch(tempArray[0]){ + case 'UNKNOWN':{ + nodeStatus[nodename] = -2; + } + break; + case 'ERROR':{ + nodeStatus[nodename] = -1; + } + break; + case 'WARNING':{ + nodeStatus[nodename] = 0; + nodePath[nodename] = tempArray[1]; + } + break; + case 'NORMAL':{ + nodeStatus[nodename] = 1; + nodePath[nodename] = tempArray[1]; + } + break; + } + } +} +/** + * draw nodes current status, there are four type: + * a. unknown(gray): can not find save data for this node + * b. error(red): get status sometime early, but can not get now + * c. warning(orange): node are heavy load + * d. normal(green): + * + * @param + * + * @return Nothing + */ +function drawGangliaNodesArea(ordertype){ + var index = 0; + var templength = 0; + var showStr = ''; + var nodename = ''; + var sortarray = new Array(); + $('#gangliaNodes').html(''); + //empty the hash + for (index in nodeStatus){ + sortarray.push([index, nodeStatus[index]]); + } + + if ('asc' == ordertype){ + sortarray.sort(statusAsc); + } + else if('des' == ordertype){ + sortarray.sort(statusDes); + } + else{ + //do nothing + } + + templength = sortarray.length; + for (index = 0; index < templength; index++){ + nodename = sortarray[index][0]; + switch(sortarray[index][1]){ + case -2:{ + showStr = '
  • '; + } + break; + case -1:{ + showStr = '
  • '; + } + break; + case 0:{ + showStr = '
  • '; + } + break; + case 1:{ + showStr = '
  • '; + } + break; + } + $('#gangliaNodes ul').append(showStr); + } + + //bind all normal and warning nodes' click event + $('.monitornormal,.monitorwarning').bind('click', function(){ + var nodename = $(this).attr('title'); + window.open('ganglianode.php?n=' + nodename + '&p=' + nodePath[nodename], + 'nodedetail','height=250,width=950,scrollbars=yes,status =no'); + }); + +} + +/** + * update all tab per minute. + * + * @param + * + * @return Nothing + */ +function updateGangliaPage(){ + if ($('#gangliaNodes').size() < 1){ + return; + } + + sendGridCurrentAjax(); + sendNodeCurrentAjax(); + + gangliaTimer = window.setTimeout('updateGangliaPage()', 60000); +} + +function statusAsc(a, b){ + return a[1] - b[1]; +} + +function statusDes(a, b){ + return b[1] - a[1]; } \ No newline at end of file diff --git a/xCAT-server/lib/xcat/plugins/web.pm b/xCAT-server/lib/xcat/plugins/web.pm index 9732c685e..027ba6b4d 100644 --- a/xCAT-server/lib/xcat/plugins/web.pm +++ b/xCAT-server/lib/xcat/plugins/web.pm @@ -14,12 +14,13 @@ use strict; require xCAT::Utils; require xCAT::MsgUtils; require xCAT::DBobjUtils; +require IO::Socket::INET; use Getopt::Long; use Data::Dumper; use LWP::Simple; use xCAT::Table; use xCAT::NodeRange; - +require XML::Parser; sub handled_commands { return { webrun => "web", }; } @@ -51,7 +52,9 @@ sub process_request { 'updatevpd' => \&web_updatevpd, 'createimage' => \&web_createimage, 'provision' => \&web_provision, - 'summary' => \&web_summay + 'summary' => \&web_summay, + 'gangliashow' => \&web_gangliaShow, + 'gangliacurrent' => \&web_gangliaLatest ); #check whether the request is authorized or not @@ -586,6 +589,287 @@ sub web_installganglia() { return; } +#get ganglia data from rrd file. +#args : +# nodeRange : the nodes' name which want to get +# time range : which period want to get, like last hour, last day, last week ..... +# metric : which monitor attribute want to get, like load_one, bytes_in, bytes_out ...... +# +#output: (till now there are 6 metic to get at one time at most) +# metric1:timestamp1,value1,timestamp2,value2,.....;metric2:timestamp1,value1,timestamp2,value2,.....;.... +sub web_gangliaShow{ + my ( $request, $callback, $sub_req ) = @_; + my $nodename = $request->{arg}->[1]; + my $timeRange = 'now-1h'; + my $resolution = 60; + my $metric = $request->{arg}->[3]; + my @nodes = (); + my $retStr = ''; + my $runInfo; + my $cmd = ''; + my $dirname = '/var/lib/ganglia/rrds/__SummaryInfo__/'; + #get the summary for this grid(the meaning of grid is referenced from ganglia ) + if ('_grid_' ne $nodename){ + $dirname = '/var/lib/ganglia/rrds/' . $nodename . '/'; + } + + if ('hour' eq $request->{arg}->[2]){ + $timeRange = 'now-1h'; + $resolution = 60; + } + elsif('day' eq $request->{arg}->[2]){ + $timeRange = 'now-1d'; + $resolution = 1800; + } + + if ('_summary_' eq $metric){ + my @metricArray = ('load_one', 'cpu_num', 'cpu_idle', 'mem_free', 'mem_total',); + my $filename = ''; + my $step = 1; + my $index = 0; + my $size = 0; + foreach my $tempmetric (@metricArray){ + my $temp = ''; + my $line = ''; + $retStr .= $tempmetric . ':'; + $filename = $dirname . $tempmetric . '.rrd'; + $cmd = "rrdtool fetch $filename -s $timeRange -r $resolution AVERAGE"; + $runInfo = xCAT::Utils->runcmd($cmd, -1, 1); + if (scalar(@$runInfo) < 3){ + $callback->({data=>'error.'}); + return; + } + #delete the first 2 lindes + shift(@$runInfo); + shift(@$runInfo); + + #we only support 60 lines for one metric, in order to reduce the data load for web gui + $size = scalar(@$runInfo); + if ($size > 60){ + $step = int($size / 60) + 1; + } + + if (($tempmetric eq 'cpu_idle') && ('_grid_' eq $nodename)){ + my $cpuidle = 0; + my $cpunum = 0; + for($index = 0; $index < $size; $index += $step){ + if ($runInfo->[$index] =~ /^(\S+): (\S+) (\S+)/){ + if (($2 eq 'NaNQ') || ($2 eq 'nan')){ + #the rrdtool fetch last outline line always nan, so no need to add into return string + if ($index == ($size - 1)){ + next; + } + $temp .= $1 . ',0,'; + } + else{ + $cpuidle = sprintf "%.2f", $2; + $cpunum = sprintf "%.2f", $3; + $temp .= $1 . ',' . (sprintf "%.2f", $cpuidle/$cpunum) . ','; + } + } + } + } + else{ + for($index = 0; $index < $size; $index += $step){ + if ($runInfo->[$index] =~ /^(\S+): (\S+).*/){ + if (($2 eq 'NaNQ') || ($2 eq 'nan')){ + #the rrdtool fetch last outline line always nan, so no need to add into return string + if ($index == ($size - 1)){ + next; + } + $temp .= $1 . ',0,'; + } + else{ + $temp .= $1 . ',' . (sprintf "%.2f", $2) . ','; + } + } + } + } + $retStr .= substr($temp, 0, -1) . ';'; + } + $retStr = substr($retStr, 0, -1); + $callback->({data=>$retStr}); + return; + } +} + +my $ganglia_return_flag = 0; +my %gangliaHash; +my $gangliaclustername; +my $ganglianodename; +#use socket to connect ganglia port to get the latest value/status +sub web_gangliaLatest{ + my ( $request, $callback, $sub_req ) = @_; + my $type = $request->{arg}->[1]; + my $groupname = ''; + my $xmlparser; + my $telnetcmd = ''; + my $connect; + my $xmloutput = ''; + + $ganglia_return_flag = 0; + $gangliaclustername = ''; + $ganglianodename = ''; + undef(%gangliaHash); + + if($request->{arg}->[2]){ + $groupname = $request->{arg}->[2]; + } + if ('grid' eq $type){ + $xmlparser = XML::Parser->new(Handlers=>{Start=>\&web_gangliaGridXmlStart, End=>\&web_gangliaXmlEnd}); + $telnetcmd = "/?filter=summary\n"; + } + elsif('node' eq $type){ + $xmlparser = XML::Parser->new(Handlers=>{Start=>\&web_gangliaNodeXmlStart, End=>\&web_gangliaXmlEnd}); + $telnetcmd = "/\n"; + } + + #use socket to telnet 127.0.0.1 8652(ganglia's interactive port) + $connect = IO::Socket::INET->new('127.0.0.1:8652'); + unless($connect){ + $callback->({'data'=>'error: connect local port failed.'}); + return; + } + + print $connect $telnetcmd; + while(<$connect>){ + $xmloutput .= $_; + } + close($connect); + + $xmlparser->parse($xmloutput); + + if ('grid' eq $type){ + web_gangliaGridLatest($callback); + } + elsif('node' eq $type){ + web_gangliaNodeLatest($callback, $groupname); + } + return; +} + +#create return data for grid current status +sub web_gangliaGridLatest{ + my $callback = shift; + my $retStr = ''; + my $timestamp = time(); + if ($gangliaHash{'load_one'}){ + $retStr .= 'load_one:' . $timestamp . ',' . $gangliaHash{'load_one'}->{'SUM'} . ';'; + } + if ($gangliaHash{'cpu_num'}){ + $retStr .= 'cpu_num:' . $timestamp . ',' . $gangliaHash{'cpu_num'}->{'SUM'} . ';'; + } + if ($gangliaHash{'cpu_idle'}){ + my $sum = $gangliaHash{'cpu_idle'}->{'SUM'}; + my $num = $gangliaHash{'cpu_idle'}->{'NUM'}; + $retStr .= 'cpu_idle:' . $timestamp . ',' . (sprintf("%.2f", $sum/$num )) . ';'; + } + if ($gangliaHash{'mem_total'}){ + $retStr .= 'mem_total:' . $timestamp . ',' . $gangliaHash{'mem_total'}->{'SUM'} . ';'; + } + if ($gangliaHash{'mem_free'}){ + $retStr .= 'mem_free:' . $timestamp . ',' . $gangliaHash{'mem_free'}->{'SUM'} . ';'; + } + + $retStr = substr($retStr, 0, -1); + $callback->({data=>$retStr}); +} + +#create return data for node current status +sub web_gangliaNodeLatest{ + my ($callback, $groupname) = @_; + my $node = ''; + my $retStr = ''; + my $timestamp = time() - 180; + my @nodes; + #get all nodes by group + if ($groupname){ + @nodes = xCAT::NodeRange::noderange($groupname, 1); + } + else{ + @nodes = xCAT::DBobjUtils->getObjectsOfType('node'); + } + foreach $node(@nodes){ + #if the node install the ganglia + if ($gangliaHash{$node}){ + my $lastupdate = $gangliaHash{$node}->{'timestamp'}; + #can not get the monitor data for too long time + if ($lastupdate < $timestamp){ + $retStr .= $node . ':ERROR,Can not get monitor data more than 3 minutes!;'; + next; + } + + if ($gangliaHash{$node}->{'load_one'} > $gangliaHash{$node}->{'cpu_num'}){ + $retStr .= $node . ':WARNING,'; + } + else{ + $retStr .= $node . ':NORMAL,'; + } + $retStr .= $gangliaHash{$node}->{'path'} . ';' + } + else{ + $retStr .= $node . ':UNKNOWN,;' ; + } + } + + $retStr = substr($retStr, 0, -1); + $callback->({data=>$retStr}); +} +#xml parser end function, do noting here +sub web_gangliaXmlEnd{ +} + +#xml parser start function for grid latest value +sub web_gangliaGridXmlStart{ + my( $parseinst, $elementname, %attrs ) = @_; + my $metricname = ''; + + #only parser grid infomation + if ($ganglia_return_flag){ + return; + } + if ('METRICS' eq $elementname){ + $metricname = $attrs{'NAME'}; + $gangliaHash{$metricname}->{'SUM'} = $attrs{'SUM'}; + $gangliaHash{$metricname}->{'NUM'} = $attrs{'NUM'}; + } + elsif ('CLUSTER' eq $elementname){ + $ganglia_return_flag = 1; + return; + } + else{ + return; + } + #only need the grid summary info, if receive cluster return directly +} + +#xml parser start function for node current status +sub web_gangliaNodeXmlStart{ + my( $parseinst, $elementname, %attrs ) = @_; + my $metricname = ''; + #save the cluster name + if('CLUSTER' eq $elementname){ + $gangliaclustername = $attrs{'NAME'}; + return; + } + elsif('HOST' eq $elementname){ + if ($attrs{'NAME'} =~ /(\S+?)\.(.*)/){ + $ganglianodename = $1; + } + else{ + $ganglianodename = $attrs{'NAME'}; + } + $gangliaHash{$ganglianodename}->{'path'} = $gangliaclustername . '/' . $attrs{'NAME'}; + $gangliaHash{$ganglianodename}->{'timestamp'} = $attrs{'REPORTED'}; + } + elsif('METRIC' eq $elementname){ + $metricname = $attrs{'NAME'}; + if (('load_one' eq $metricname) || ('cpu_num' eq $metricname)){ + $gangliaHash{$ganglianodename}->{$metricname} = $attrs{'VAL'}; + } + } +} + sub web_rmcmonStart { my ( $request, $callback, $sub_req ) = @_; my $nodeRange = $request->{arg}->[1];