/** * Global variables */ var nodesTab; // Nodes tabs var origAttrs = new Object(); // Original node attributes var nodeAttrs; // Node attributes var nodesList; // Node list var nodesTableId = 'nodesDatatable'; // Nodes datatable ID /** * Set node tab * * @param tab * Tab object * @return Nothing */ function setNodesTab(tab) { nodesTab = tab; } /** * Get node tab * * @return Tab object */ function getNodesTab() { return nodesTab; } /** * Get node list * * @return Node list */ function getNodesList() { return nodesList; } /** * Get nodes table ID * * @return Nodes table ID */ function getNodesTableId() { return nodesTableId; } /** * Load nodes page */ function loadNodesPage() { // If groups are not already loaded if (!$('#groups').length) { // Create a groups division var groups = $('<div id="groups"></div>'); var nodes = $('<div id="nodes"></div>'); $('#content').append(groups); $('#content').append(nodes); // Create loader and info bar groups.append(createLoader()); // Get groups $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'extnoderange', tgt : '/.*', args : 'subgroups', msg : '' }, // Load groups success : function(data){ loadGroups(data); var cookieGroup = $.cookie('selectgrouponnodes'); if (cookieGroup) { $('#groups .groupdiv div').each(function(){ if ($(this).text() == cookieGroup){ $(this).trigger('click'); return false; } }); } else { // Trigger the first group click event $('#groups .groupdiv div').eq(0).trigger('click'); } } }); } } /** * Show cluster summary in pie charts * * @param groupName Group name */ function loadPieSummary(groupName){ var summaryTable = '<table style="border: 0px none;">' + '<tr>' + '<td><div id="statuspie" class="summarypie"></div></td>' + '<td><div id="ospie" class="summarypie"></div></td>' + '<td><div id="archpie" class="summarypie"></div></td>' + '</tr>' + '<tr>' + '<td><div id="provmethodpie" class="summarypie"></td>' + '<td><div id="nodetypepie" class="summarypie"></div></td>' + '</tr></table>'; $('#summaryTab').append(summaryTable); $('#summaryTab .summarypie').append(createLoader()); $.ajax({ url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'summary;' + groupName, msg : '' }, success:function(data) { for (var i in data.rsp) { drawPieSummary(i, data.rsp[i]); } } }); } /** * Get nodes information and draw pie chart * * @param index Node index * @param valuePair Node information key value pairing */ function drawPieSummary(index, valuePair){ var position = 0; var key = ''; var val = ''; var chartTitle = ''; var dataArray = []; var tempArray = []; var container = $('#summaryTab .summarypie').eq(index); position = valuePair.indexOf('='); chartTitle = valuePair.substr(0, position); tempArray = valuePair.substr(position + 1).split(';'); for (var i in tempArray) { position = tempArray[i].indexOf(':'); key = tempArray[i].substr(0, position); val = Number(tempArray[i].substr(position + 1)); dataArray.push([key,val]); } container.empty(); var plot = $.jqplot(container.attr('id'), [dataArray], { title: chartTitle, 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' } }); } /** * Load groups * * @param data Data returned from HTTP request */ function loadGroups(data) { // Remove loader $('#groups').find('img').remove(); // Save group in cookie var groups = data.rsp; setGroupsCookies(data); // Create a list of groups $('#groups').append('<div class="grouplabel">Groups</div>'); var grouplist= $('<div class="groupdiv"></div>'); // Create a link for each group for (var i = 0; i < groups.length; i++) { grouplist.append('<div>' + groups[i] + '</div>'); } $('#groups').append(grouplist); // Bind the click event $('#groups .groupdiv div').bind('click', function(){ var thisGroup = $(this).text(); $('#groups .groupdiv div').removeClass('selectgroup'); $(this).addClass('selectgroup'); // Save selected group into cookie $.cookie('selectgrouponnodes', thisGroup, { expires: 7 }); drawNodesArea(thisGroup,'',thisGroup); }); // Make a link to add nodes $('#groups').append('<div class="actionDiv" id="adddiv"></div>'); $('#groups #adddiv').append(mkAddNodeLink()); } /** * Empty the nodes area and add three tabs for nodes result * * @param targetgroup The name range for nodels command * @param cmdargs Filter arguments for nodels command * @param message The useful information from the HTTP request */ function drawNodesArea(targetgroup, cmdargs, message){ // Clear nodes division $('#nodes').empty(); // Create a tab for this group var tab = new Tab('nodesPageTabs'); setNodesTab(tab); tab.init(); $('#nodes').append(tab.object()); tab.add('summaryTab', 'Summary', '', false); tab.add('nodesTab', 'Nodes', '', false); tab.add('graphTab', 'Graphic', '', false); // Load nodes table when tab is selected $('#nodesPageTabs').bind('tabsselect', function(event, ui) { // Load summary when tab is selected if (!$('#summaryTab').children().length && ui.index == 0) { loadPieSummary(targetgroup); } // Load nodes table when tab is selected else if (!$('#nodesTab').children().length && ui.index == 1) { // Create loader $('#nodesTab').append($('<center></center>').append(createLoader())); // To improve performance, get all nodes within selected group // Get node definitions only for first 50 nodes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'nodels', tgt : targetgroup, args : cmdargs, msg : message }, /** * Get node definitions for first 50 nodes * * @param data Data returned from HTTP request */ success : function(data) { var rsp = data.rsp; var group = data.msg; // Save nodes in a list so it can be accessed later nodesList = new Array(); for (var i in rsp) { if (rsp[i][0]) { nodesList.push(rsp[i][0]); } } // Sort nodes list nodesList.sort(); // Get first 50 nodes var nodes = ''; for (var i = 0; i < nodesList.length; i++) { if (i > 49) { break; } nodes += nodesList[i] + ','; } // Remove last comma nodes = nodes.substring(0, nodes.length-1); // Get nodes definitions $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'lsdef', tgt : '', args : nodes, msg : targetgroup }, success : loadNodes }); } }); } // Load graphical layout when tab is selected else if (!$('#graphTab').children().length && ui.index == 2) { // For the graphical tab, check the graphical data first createPhysicalLayout(nodesList); } }); // Get last view (if any) // This can be summary, nodes, or graphic if ($.cookie('tabindex_history')) { var order = $.cookie('tabindex_history').split(','); order[0] = parseInt(order[0]); order[1] = parseInt(order[1]); if (order[0] == 0 || order[1] == 0) { // For some reason, you cannot trigger a select of index 0 loadPieSummary(targetgroup); } else if (order[0] == 1 || order[0] == 2) { $('#nodesPageTabs').tabs('select', order[0]); } else if (order[1] == 1 || order[1] == 2) { $('#nodesPageTabs').tabs('select', order[1]); } else { loadPieSummary(targetgroup); } } else { loadPieSummary(targetgroup); } } /** * Make a link to add nodes * * @returns Link to add nodes */ function mkAddNodeLink() { // Create link to add nodes var addNodeLink = $('<a title="Add a node or a node range to xCAT">+ Add Node</a>'); addNodeLink.click(function() { // Create info bar var info = createInfoBar('Select the hardware management for the new node range'); // Create form to add node var addNodeForm = $('<div class="form"></div>'); addNodeForm.append(info); addNodeForm.append('<div><label for="mgt">Hardware management:</label>' + '<select id="mgt" name="mgt">' + '<option value="esx">ESX</option>' + '<option value="kvm">KVM</option>' + '<option value="zvm">z\/VM</option>' + '<option value="ipmi">iDataPlex</option>' + '<option value="blade">BladeCenter</option>' + '<option value="hmc">System p</option>' // Documentation refers to 'IBM System p' (where p is NOT capitalized) + '</select>' + '</div>'); // Create advanced link to set advanced node properties var advanced = $('<div></div>'); var advancedLnk = $('<a>Advanced</a>').css({ 'cursor': 'pointer', 'color': '#0000FF' }); advancedLnk.click(function() { // Get node attributes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'lsdef', tgt : '', args : '-t;node;-h', msg : '' }, /** * Set node attributes and open dialog * * @param data Data returned from HTTP request */ success : function(data) { // Save node attributes setNodeAttrs(data); // Open a dialog to set node attributes openSetAttrsDialog(); } }); // Close dialog addNodeForm.dialog('close'); }); advanced.append(advancedLnk); addNodeForm.append(advanced); // Open dialog to add node addNodeForm.dialog({ modal: true, width: 400, title:'Add node', close: function(){$(this).remove();}, buttons: { 'Ok': function(){ // Get hardware management var mgt = $(this).find('select[name=mgt]').val(); var plugin; switch(mgt) { case "kvm": plugin = new kvmPlugin(); break; case "esx": plugin = new esxPlugin(); break; case "blade": plugin = new bladePlugin(); break; case "hmc": plugin = new hmcPlugin(); break; case "ipmi": plugin = new ipmiPlugin(); break; case "zvm": plugin = new zvmPlugin(); break; } plugin.addNode(); $(this).dialog('close'); }, 'Cancel': function(){ $(this).dialog('close'); } } }); }); // Generate tooltips addNodeLink.tooltip({ position: 'center right', offset: [-2, 10], effect: 'fade', opacity: 0.7, predelay: 800 }); return addNodeLink; } /** * Load nodes belonging to a given group * * @param data Data returned from HTTP request */ function loadNodes(data) { // Clear the tab before inserting the table $('#nodesTab').children().remove(); // Data returned var rsp = data.rsp; // Group name var group = data.msg; // Hash of Node attributes var attrs = new Object(); // Node attributes var headers = new Object(); // Variable to send command and request node status var getNodeStatus = true; // Clear hash table containing node attributes origAttrs = ''; var node, args; for (var i in rsp) { // Get node name if (rsp[i].indexOf('Object name:') > -1) { var temp = rsp[i].split(': '); node = jQuery.trim(temp[1]); // Create a hash for the node attributes attrs[node] = new Object(); i++; } // Get key and value args = rsp[i].split('=', 2); var key = jQuery.trim(args[0]); var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1)); // Create a hash table attrs[node][key] = val; headers[key] = 1; // If node status is available if (key == 'status') { // Do not request node status getNodeStatus = false; } } // Add nodes that are not in data returned for (var i in nodesList) { if (!attrs[nodesList[i]]) { // Create attributes list and save node name attrs[nodesList[i]] = new Object(); attrs[nodesList[i]]['node'] = nodesList[i]; } } // Save attributes in hash table origAttrs = attrs; // Sort headers var sorted = new Array(); for (var key in headers) { // Do not put comments and status in twice if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) { sorted.push(key); } } sorted.sort(); // Add column for check box, node, ping, power, monitor, and comments sorted.unshift('<input type="checkbox" onclick="selectAllCheckbox(event, $(this))">', 'node', '<span><a>status</a></span><img src="images/loader.gif"></img>', '<span><a>power</a></span><img src="images/loader.gif" style="display: none;"></img>', '<span><a>monitor</a></span><img src="images/loader.gif" style="display: none;"></img>', 'comments'); // Create a datatable var nodesTable = new DataTable(nodesTableId); nodesTable.init(sorted); // Go through each node for (var node in attrs) { // Create a row var row = new Array(); // Create a check box, node link, and get node status var checkBx = '<input type="checkbox" name="' + node + '"/>'; var nodeLink = $('<a class="node" id="' + node + '">' + node + '</a>').bind('click', loadNode); // If there is no status attribute for the node, do not try to access hash table // else the code will break var status = ''; if (attrs[node]['status']) { status = attrs[node]['status'].replace('sshd', 'ping'); } // Push in checkbox, node, status, monitor, and power row.push(checkBx, nodeLink, status, '', ''); // If the node attributes are known (i.e the group is known) if (attrs[node]['groups']) { // Put in comments var comments = attrs[node]['usercomment']; // If no comments exists, show 'No comments' and set icon image source var iconSrc; if (!comments) { comments = 'No comments'; iconSrc = 'images/nodes/ui-icon-no-comment.png'; } else { iconSrc = 'images/nodes/ui-icon-comment.png'; } // Create comments icon var tipID = node + 'Tip'; var icon = $('<img id="' + tipID + '" src="' + iconSrc + '"></img>').css({ 'width': '18px', 'height': '18px' }); // Create tooltip var tip = createCommentsToolTip(comments); var col = $('<span></span>').append(icon); col.append(tip); row.push(col); // Generate tooltips icon.tooltip({ position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.8, relative: true, delay: 500 }); } else { // Do not put in comments if attributes are not known row.push(''); } // Go through each header for (var i = 6; i < sorted.length; i++) { // Add the node attributes to the row var key = sorted[i]; // Do not put comments and status in twice if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) { var val = attrs[node][key]; if (val) { row.push(val); } else { row.push(''); } } } // Add the row to the table nodesTable.add(row); } // Clear the tab before inserting the table $('#nodesTab').children().remove(); // Create info bar for nodes tab var info = createInfoBar('Double-click on a cell to edit a node\'s properties. Click outside the table to save changes. Hit the Escape key to ignore changes.'); $('#nodesTab').append(info); // Create action bar var actionBar = $('<div class="actionBar"></div>'); /** * Create menu for actions to perform against a given node */ // Power on var powerOnLnk = $('<a>Power on</a>'); powerOnLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { powerNode(tgtNodes, 'on'); } }); // Power off var powerOffLnk = $('<a>Power off</a>'); powerOffLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { powerNode(tgtNodes, 'off'); } }); // Turn monitoring on var monitorOnLnk = $('<a>Monitor on</a>'); monitorOnLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { monitorNode(tgtNodes, 'on'); } }); // Turn monitoring off var monitorOffLnk = $('<a>Monitor off</a>'); monitorOffLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { monitorNode(tgtNodes, 'off'); } }); // Clone var cloneLnk = $('<a>Clone</a>'); cloneLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId).split(','); for (var i in tgtNodes) { var mgt = getNodeAttr(tgtNodes[i], 'mgt'); // Create an instance of the plugin var plugin; switch(mgt) { case "blade": plugin = new bladePlugin(); break; case "fsp": plugin = new fspPlugin(); break; case "hmc": plugin = new hmcPlugin(); break; case "ipmi": plugin = new ipmiPlugin(); break; case "ivm": plugin = new ivmPlugin(); break; case "zvm": plugin = new zvmPlugin(); break; } plugin.loadClonePage(tgtNodes[i]); } }); // Delete var deleteLnk = $('<a>Delete</a>'); deleteLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadDeletePage(tgtNodes); } }); // Unlock var unlockLnk = $('<a>Unlock</a>'); unlockLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadUnlockPage(tgtNodes); } }); // Run script var scriptLnk = $('<a>Run script</a>'); scriptLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadScriptPage(tgtNodes); } }); // Update var updateLnk = $('<a>Update</a>'); updateLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadUpdatenodePage(tgtNodes); } }); // Set boot state var setBootStateLnk = $('<a>Set boot state</a>'); setBootStateLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadNodesetPage(tgtNodes); } }); // Boot to network var boot2NetworkLnk = $('<a>Boot to network</a>'); boot2NetworkLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadNetbootPage(tgtNodes); } }); // Provision node var provisionLnk = $('<a>Provision</a>'); provisionLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes){ // Jump directly to the provision page jump2Provision(tgtNodes); } }); // Remote console var rcons = $('<a>Open console</a>'); rcons.bind('click', function(event){ var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { loadRconsPage(tgtNodes); } }); // Edit properties var editProps = $('<a>Edit properties</a>'); editProps.bind('click', function(event){ var tgtNodes = getNodesChecked(nodesTableId).split(','); for (var i in tgtNodes) { editNodeProps(tgtNodes[i]); } }); // Install Ganglia var installMonLnk = $('<a>Install monitoring</a>'); installMonLnk.click(function() { var tgtNodes = getNodesChecked(nodesTableId); if (tgtNodes) { installGanglia(tgtNodes); } }); // Actions var actionsLnk = '<a>Actions</a>'; var actsMenu = createMenu([cloneLnk, deleteLnk, monitorOnLnk, monitorOffLnk, powerOnLnk, powerOffLnk, scriptLnk]); // Configurations var configLnk = '<a>Configuration</a>'; var configMenu = createMenu([unlockLnk, updateLnk, editProps, installMonLnk]); // Provision var provLnk = '<a>Provision</a>'; var provMenu = createMenu([boot2NetworkLnk, setBootStateLnk, rcons, provisionLnk]); // Create an action menu var actionsMenu = createMenu([ [ actionsLnk, actsMenu ], [ configLnk, configMenu ], [ provLnk, provMenu ] ]); actionsMenu.superfish(); actionsMenu.css('display', 'inline-block'); actionBar.append(actionsMenu); // Set correct theme for action menu actionsMenu.find('li').hover(function() { setMenu2Theme($(this)); }, function() { setMenu2Normal($(this)); }); // Insert action bar and nodes datatable $('#nodesTab').append(nodesTable.object()); // Turn table into a datatable var nodesDatatable = $('#' + nodesTableId).dataTable({ 'iDisplayLength': 50, 'bLengthChange': false, "sScrollX": "100%", "bAutoWidth": true, "fnInitComplete": function() { adjustColumnSize(nodesTableId); } }); // Filter table when enter key is pressed $('#' + nodesTableId + '_filter input').unbind(); $('#' + nodesTableId + '_filter input').bind('keyup', function(e){ if (e.keyCode == 13) { var table = $('#' + nodesTableId).dataTable(); table.fnFilter($(this).val()); // If there are nodes found, get the node attributes if (!$('#' + nodesTableId + ' .dataTables_empty').length) { getNodeAttrs(group); } } }); // Load node definitions when next or previous buttons are clicked $('#' + nodesTableId + '_next, #' + nodesTableId + '_previous').click(function() { getNodeAttrs(group); }); /** * Change how datatable behaves */ // Do not sort ping, power, and comment column var cols = $('#' + nodesTableId + ' thead tr th').click(function() { getNodeAttrs(group); }); var checkboxCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(0)'); var pingCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); var monitorCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); var commentCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(5)'); checkboxCol.unbind('click'); pingCol.unbind('click'); powerCol.unbind('click'); monitorCol.unbind('click'); commentCol.unbind('click'); // Create enough space for loader to be displayed // Center align power, ping, and comments $('#' + nodesTableId + ' td:nth-child(3),td:nth-child(4),td:nth-child(5)').css({ 'text-align': 'center' }); // No minimum width for comments column $('#' + nodesTableId + ' tbody tr td:nth-child(6)').css('text-align', 'center'); // Instead refresh the node, power, and monitor status pingCol.find('span a').click(function() { refreshNodeStatus(group, nodesTableId); }); powerCol.find('span a').click(function() { refreshPowerStatus(group, nodesTableId); }); monitorCol.find('span a').click(function() { refreshGangliaStatus(group, nodesTableId); }); // Create a division to hold actions menu var menuDiv = $('<div id="' + nodesTableId + '_menuDiv" class="menuDiv"></div>'); $('#' + nodesTableId + '_wrapper').prepend(menuDiv); menuDiv.append(actionBar); $('#' + nodesTableId + '_filter').appendTo(menuDiv); // Create tooltip for status var tooltipConf = { position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.8, relative: true, predelay: 800 }; var pingTip = createStatusToolTip(); pingCol.find('span').append(pingTip); pingCol.find('span a').tooltip(tooltipConf); // Create tooltip for power var powerTip = createPowerToolTip(); powerCol.find('span').append(powerTip); powerCol.find('span a').tooltip(tooltipConf); // Create tooltip for monitor var monitorTip = createMonitorToolTip(); monitorCol.find('span').append(monitorTip); monitorCol.find('span a').tooltip(tooltipConf); /** * Enable editable columns */ // Do not make 1st, 2nd, 3rd, 4th, 5th, or 6th column editable $('#' + nodesTableId + ' td:not(td:nth-child(1),td:nth-child(2),td:nth-child(3),td:nth-child(4),td:nth-child(5),td:nth-child(6))').editable( function(value, settings) { // If users did not make changes, return the value directly // jeditable saves the old value in this.revert if ($(this).attr('revert') == value){ return value; } // Get column index var colPos = this.cellIndex; // Get row index var dTable = $('#' + nodesTableId).dataTable(); var rowPos = dTable.fnGetPosition(this.parentNode); // Update datatable dTable.fnUpdate(value, rowPos, colPos, false); // Get table headers var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); // Get node name var node = $(this).parent().find('td a.node').text(); // Get attribute name var attrName = jQuery.trim(headers.eq(colPos).text()); // Get column value var value = $(this).text(); // Build argument var args = attrName + '=' + value; // Send command to change node attributes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'chdef', tgt : '', args : '-t;node;-o;' + node + ';' + args, msg : 'out=nodesTab;tgt=' + node }, success: showChdefOutput }); // Save the data into global origAttrs origAttrs[node][attrName] = value; return value; }, { onblur : 'submit', // Clicking outside editable area submits changes type : 'textarea', placeholder: ' ', event : "dblclick", // Double click and edit height : '30px' // The height of the text area }); /** * Get the node status and definable node attributes */ // If request to get node status is made if (getNodeStatus) { var tgt = getNodesShown(nodesTableId); // Get node status $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'nodestat', tgt : tgt, args : '-u', msg : '' }, success : loadNodeStatus }); } else { // Hide status loader var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); statCol.find('img').hide(); adjustColumnSize(nodesTableId); } if (undefined == nodeAttrs){ // Get definable node attributes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'lsdef', tgt : '', args : '-t;node;-h', msg : '' }, success : setNodeAttrs }); } /** * Additional ajax requests need to be made for zVM * load advanced information based on hardware architecture */ advancedLoad(group); } /** * Get nodes currently shown in datatable * * @param tableId Datatable ID * @return String of nodes shown */ function getNodesShown(tableId) { // String of nodes shown var shownNodes = ''; // Get rows of shown nodes var nodes = $('#' + tableId + ' tbody tr'); // Go through each row var cols; for (var i = 0; i < nodes.length; i++) { // Get second column containing node name cols = nodes.eq(i).find('td'); shownNodes += cols.eq(1).text() + ','; } // Remove last comma shownNodes = shownNodes.substring(0, shownNodes.length-1); return shownNodes; } /** * Get attributes for nodes not yet initialized * * @param group Group name */ function getNodeAttrs(group) { // Get datatable headers and rows var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); var nodes = $('#' + nodesTableId + ' tbody tr'); // Find group column var head, groupsCol; for (var i = 0; i < headers.length; i++) { head = headers.eq(i).html(); if (head == 'groups') { groupsCol = i; break; } } // Check if groups definition is set var node, cols; var tgtNodes = ''; for (var i = 0; i < nodes.length; i++) { cols = nodes.eq(i).find('td'); if (!cols.eq(groupsCol).html()) { node = cols.eq(1).text(); tgtNodes += node + ','; } } // If there are node definitions to load if (tgtNodes) { // Remove last comma tgtNodes = tgtNodes.substring(0, tgtNodes.length-1); // Get node definitions $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'lsdef', tgt : '', args : tgtNodes, msg : group }, success : addNodes2Table }); // Create dialog to indicate table is updating var update = $('<div id="updatingDialog"></div>'); update.append(createInfoBar('Updating table <img src="images/loader.gif"/>')); update.dialog({ title: 'Updating', modal: true, width: 300, position: 'center' }); } } /** * Add nodes to datatable * * @param data Data returned from HTTP request */ function addNodes2Table(data) { // Data returned var rsp = data.rsp; // Group name var group = data.msg; // Hash of node attributes var attrs = new Object(); // Node attributes var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr th'); // Variable to send command and request node status var getNodeStatus = true; // Go through each attribute var node, args; for (var i in rsp) { // Get node name if (rsp[i].indexOf('Object name:') > -1) { var temp = rsp[i].split(': '); node = jQuery.trim(temp[1]); // Create a hash for node attributes attrs[node] = new Object(); i++; } // Get key and value args = rsp[i].split('=', 2); var key = jQuery.trim(args[0]); var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1, rsp[i].length)); // Create a hash table attrs[node][key] = val; // Save attributes in original hash table origAttrs[node][key] = val; // If node status is available if (key == 'status') { // Do not request node status getNodeStatus = false; } } // Set the first five headers var headersCol = new Object(); headersCol['node'] = 1; headersCol['status'] = 2; headersCol['power'] = 3; headersCol['monitor'] = 4; headersCol['comments'] = 5; // Go through each header for (var i = 6; i < headers.length; i++) { // Get the column index headersCol[headers.eq(i).html()] = i; } // Go through each node var datatable = $('#' + nodesTableId).dataTable(); var rows = datatable.fnGetData(); for (var node in attrs) { // Get row containing node var nodeRowPos = 0; for (var i in rows) { // If column contains node if (rows[i][1].indexOf('>' + node + '<') > -1) { nodeRowPos = i; break; } } // Get node status var status = ''; if (attrs[node]['status']){ status = attrs[node]['status'].replace('sshd', 'ping'); } rows[nodeRowPos][headersCol['status']] = status; // Go through each header for (var key in headersCol) { // Do not put comments and status in twice if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) { var val = attrs[node][key]; if (val) { rows[nodeRowPos][headersCol[key]] = val; } } } // Update row datatable.fnUpdate(rows[nodeRowPos], nodeRowPos, 0, false); // Insert node comments // This is done after datatable is updated because // you cannot insert an object using fnUpdate() var comments = attrs[node]['usercomment']; // If no comments exists, show 'No comments' and // set icon image source var iconSrc; if (!comments) { comments = 'No comments'; iconSrc = 'images/nodes/ui-icon-no-comment.png'; } else { iconSrc = 'images/nodes/ui-icon-comment.png'; } // Create icon for node comments var tipID = node + 'Tip'; var commentsCol = $('#' + node).parent().parent().find('td').eq(5); // Create tooltip var icon = $('<img id="' + tipID + '" src="' + iconSrc + '"></img>').css({ 'width': '18px', 'height': '18px' }); var tip = createCommentsToolTip(comments); var span = $('<span></span>').append(icon); span.append(tip); commentsCol.append(span); // Generate tooltips icon.tooltip({ position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.8, relative: true, delay: 500 }); } // Enable node link $('.node').bind('click', loadNode); // Close dialog for updating table $('.ui-dialog-content').dialog('close'); /** * Enable editable columns */ // Do not make 1st, 2nd, 3rd, 4th, 5th, or 6th column editable $('#' + nodesTableId + ' td:not(td:nth-child(1),td:nth-child(2),td:nth-child(3),td:nth-child(4),td:nth-child(5),td:nth-child(6))').editable( function(value, settings) { //if users did not do changes, return the value directly //jeditable save the old value in this.revert if ($(this).attr('revert') == value){ return value; } // Get column index var colPos = this.cellIndex; // Get row index var dTable = $('#' + nodesTableId).dataTable(); var rowPos = dTable.fnGetPosition(this.parentNode); // Update datatable dTable.fnUpdate(value, rowPos, colPos, false); // Get table headers var headers = $('#' + nodesTableId + ' thead tr th'); // Get node name var node = $(this).parent().find('td a.node').text(); // Get attribute name var attrName = jQuery.trim(headers.eq(colPos).text()); // Get column value var value = $(this).text(); // Build argument var args = attrName + '=' + value; // Send command to change node attributes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'chdef', tgt : '', args : '-t;node;-o;' + node + ';' + args, msg : 'out=nodesTab;tgt=' + node }, success: showChdefOutput }); return value; }, { onblur : 'submit', // Clicking outside editable area submits changes type : 'textarea', placeholder: ' ', event : 'dblclick', height : '30px' // The height of the text area }); // If request to get node status is made if (getNodeStatus) { // Get node status $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'nodestat', tgt : group, args : '-u', msg : '' }, success : loadNodeStatus }); } else { // Hide status loader var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); statCol.find('img').hide(); } /** * Additional ajax requests need to be made for zVM */ advancedLoad(group); adjustColumnSize(nodesTableId); } /** * Load the status of Ganglia for a given group * * @param data Data returned from HTTP request */ function loadGangliaStatus(data) { // Get datatable var datatable = $('#' + nodesTableId).dataTable(); var ganglia = data.rsp; var rowNum, node, status; for ( var i in ganglia) { // ganglia[0] = nodeName and ganglia[1] = state node = jQuery.trim(ganglia[i][0]); status = jQuery.trim(ganglia[i][1]); if (node) { // Get the row containing the node rowNum = findRow(node, '#' + nodesTableId, 1); // Update the power status column datatable.fnUpdate(status, rowNum, 4); } } // Hide Ganglia loader var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); gangliaCol.find('img').hide(); adjustColumnSize(nodesTableId); } /** * Refresh the status of Ganglia for each node * * @param group Group name */ function refreshGangliaStatus(group) { // Show ganglia loader var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); gangliaCol.find('img').show(); // Get power status for nodes shown var nodes = getNodesShown(nodesTableId); // Get the status of Ganglia $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'gangliastatus;' + nodes, msg : '' }, success : loadGangliaStatus }); } /** * Load power status for each node * * @param data Data returned from HTTP request */ function loadPowerStatus(data) { var dTable = $('#' + nodesTableId).dataTable(); var power = data.rsp; var rowPos, node, status, args; for (var i in power) { // power[0] = nodeName and power[1] = state args = power[i].split(':'); node = jQuery.trim(args[0]); status = jQuery.trim(args[1]); // Get the row containing the node rowPos = findRow(node, '#' + nodesTableId, 1); // Update the power status column dTable.fnUpdate(status, rowPos, 3, false); } // Hide power loader var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); powerCol.find('img').hide(); adjustColumnSize(nodesTableId); } /** * Refresh power status for each node * * @param group Group name * @param tableId Table to update node status */ function refreshPowerStatus(group, tableId) { // Show power loader var powerCol = $('#' + tableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); powerCol.find('img').show(); // Get power status for nodes shown var nodes = getNodesShown(tableId); // Get power status $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'rpower', tgt : nodes, args : 'stat', msg : '' }, success : loadPowerStatus }); } /** * Load node status for each node * * @param data Data returned from HTTP request */ function loadNodeStatus(data) { var dTable = $('#' + nodesTableId).dataTable(); var rsp = data.rsp; var args, rowPos, node, status; // Get all nodes within datatable for (var i in rsp) { args = rsp[i].split(':'); // args[0] = node and args[1] = status node = jQuery.trim(args[0]); status = jQuery.trim(args[1]).replace('sshd', 'ping'); // Get row containing node rowPos = findRow(node, '#' + nodesTableId, 1); // Update ping status column dTable.fnUpdate(status, rowPos, 2, false); } // Hide status loader var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); statCol.find('img').hide(); adjustColumnSize(nodesTableId); } /** * Refresh ping status for each node * * @param group Group name * @param tableId Table to update node status */ function refreshNodeStatus(group, tableId) { // Show ping loader var pingCol = $('#' + tableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); pingCol.find('img').show(); // Get power status for nodes shown var nodes = getNodesShown(tableId); // Get the node status $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'nodestat', tgt : nodes, args : '-u', msg : '' }, success : loadNodeStatus }); } /** * Load inventory for given node * * @param e Windows event */ function loadNode(e) { if (!e) { e = window.event; } // Get node that was clicked var node = (e.target) ? e.target.id : e.srcElement.id; var mgt = getNodeAttr(node, 'mgt'); // Create an instance of the plugin var plugin; switch(mgt) { case "blade": plugin = new bladePlugin(); break; case "fsp": plugin = new fspPlugin(); break; case "hmc": plugin = new hmcPlugin(); break; case "ipmi": plugin = new ipmiPlugin(); break; case "ivm": plugin = new ivmPlugin(); break; case "zvm": plugin = new zvmPlugin(); break; } // Get tab area where a new tab will be inserted var myTab = getNodesTab(); var inst = 0; var newTabId = 'nodeTab' + inst; while ($('#' + newTabId).length) { // If one already exists, generate another one inst = inst + 1; newTabId = 'nodeTab' + inst; } // Reset node process $.cookie(node + 'Processes', 0); // Add new tab, only if one does not exist var loader = createLoader(newTabId + 'TabLoader'); loader = $('<center></center>').append(loader); myTab.add(newTabId, node, loader, true); // Get node inventory var msg = 'out=' + newTabId + ',node=' + node; $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'rinv', tgt : node, args : 'all', msg : msg }, success : plugin.loadInventory }); // Select new tab myTab.select(newTabId); } /** * Unlock a node by setting the ssh keys * * @param tgtNodes Nodes to unlock */ function loadUnlockPage(tgtNodes) { // Get nodes tab var tab = getNodesTab(); // Generate new tab ID var instance = 0; var newTabId = 'unlockTab' + instance; while ($('#' + newTabId).length) { // If one already exists, generate another one instance = instance + 1; newTabId = 'unlockTab' + instance; } var unlockForm = $('<div class="form"></div>'); // Create status bar, hide on load var statBarId = 'unlockStatusBar' + instance; var statusBar = createStatusBar(statBarId).hide(); unlockForm.append(statusBar); // Create loader var loader = createLoader(''); statusBar.find('div').append(loader); // Create info bar var infoBar = createInfoBar('Give the root password for this node range to setup its SSH keys.'); unlockForm.append(infoBar); unlockForm.append('<div><label>Target node range:</label><input type="text" id="node" name="node" readonly="readonly" value="' + tgtNodes + '" title="The node or node range to unlock"/></div>'); unlockForm.append('<div><label>Password:</label><input type="password" id="password" name="password" title="The root password to unlock this node"/></div>'); // Generate tooltips unlockForm.find('div input[title]').tooltip({ position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.7, predelay: 800, events : { def : "mouseover,mouseout", input : "mouseover,mouseout", widget : "focus mouseover,blur mouseout", tooltip : "mouseover,mouseout" } }); /** * Ok */ var okBtn = createButton('Ok'); okBtn.click(function() { // Remove any warning messages $(this).parent().parent().find('.ui-state-error').remove(); // If a password is given var password = $('#' + newTabId + ' input[name=password]').css('border', 'solid #BDBDBD 1px'); if (password.val()) { // Setup SSH keys $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'unlock;' + tgtNodes + ';' + password.val(), msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes }, success : updateStatusBar }); // Show status bar statusBar.show(); // Disable all inputs and Ok button $('#' + newTabId + ' input').attr('disabled', 'disabled'); $(this).attr('disabled', 'true'); } else { // Show warning message var warn = createWarnBar('You are missing some values!'); warn.prependTo($(this).parent().parent()); password.css('border', 'solid #FF0000 1px'); } }); unlockForm.append(okBtn); tab.add(newTabId, 'Unlock', unlockForm, true); tab.select(newTabId); } /** * Load script page * * @param tgtNodes Targets to run script against */ function loadScriptPage(tgtNodes) { // Get nodes tab var tab = getNodesTab(); // Generate new tab ID var inst = 0; var newTabId = 'scriptTab' + inst; while ($('#' + newTabId).length) { // If one already exists, generate another one inst = inst + 1; newTabId = 'scriptTab' + inst; } // Open new tab // Create remote script form var scriptForm = $('<div class="form"></div>'); // Create status bar var barId = 'scriptStatusBar' + inst; var statBar = createStatusBar(barId); statBar.hide(); scriptForm.append(statBar); // Create loader var loader = createLoader('scriptLoader' + inst); statBar.find('div').append(loader); // Create info bar var infoBar = createInfoBar('Load a script to run against this node range.'); scriptForm.append(infoBar); // Target node or group var tgt = $('<div><label for="target">Target node range:</label><input type="text" name="target" value="' + tgtNodes + '" title="The node or node range to run a given script against"/></div>'); scriptForm.append(tgt); // Upload file var upload = $('<form action="lib/upload.php" method="post" enctype="multipart/form-data"></form>'); var label = $('<label for="file">Remote file:</label>'); var file = $('<input type="file" name="file" id="file"/>'); var subBtn = createButton('Load'); upload.append(label); upload.append(file); upload.append(subBtn); scriptForm.append(upload); // Generate tooltips scriptForm.find('div input[title]').tooltip({ position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.7, predelay: 800, events : { def : "mouseover,mouseout", input : "mouseover,mouseout", widget : "focus mouseover,blur mouseout", tooltip : "mouseover,mouseout" } }); // Script var script = $('<div><label>Script:</label><textarea/>'); scriptForm.append(script); // Ajax form options var options = { // Output to text area target : '#' + newTabId + ' textarea' }; upload.ajaxForm(options); /** * Run */ var runBtn = createButton('Run'); runBtn.click(function() { // Remove any warning messages $(this).parent().parent().find('.ui-state-error').remove(); // Get script to run var textarea = $('#' + newTabId + ' textarea').css('border', 'solid #BDBDBD 1px'); // If no inputs are empty if (textarea.val()) { // Run script runScript(inst); } else { // Show warning message var warn = createWarnBar('You are missing some values'); warn.prependTo($(this).parent().parent()); textarea.css('border', 'solid #FF0000 1px'); } }); scriptForm.append(runBtn); // Append to discover tab tab.add(newTabId, 'Script', scriptForm, true); // Select new tab tab.select(newTabId); } /** * Sort a list * * @return Sorted list */ jQuery.fn.sort = function() { return this.pushStack([].sort.apply(this, arguments), []); }; function sortAlpha(a, b) { return a.innerHTML > b.innerHTML ? 1 : -1; }; /** * Power on a given node * * @param node Node to power on or off * @param power2 Power node to given state */ function powerNode(node, power2) { // Show power loader var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); powerCol.find('img').show(); node = node.replace('Power', ''); $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'rpower', tgt : node, args : power2, msg : node }, success : updatePowerStatus }); } /** * Load delete node page * * @param tgtNodes Nodes to delete */ function loadDeletePage(tgtNodes) { // Get nodes tab var myTab = getNodesTab(); // Generate new tab ID var inst = 0; newTabId = 'deleteTab' + inst; while ($('#' + newTabId).length) { // If one already exists, generate another one inst = inst + 1; newTabId = 'deleteTab' + inst; } // Create status bar, hide on load var statBarId = 'deleteStatusBar' + inst; var statBar = createStatusBar(statBarId).hide(); // Create loader var loader = createLoader(''); statBar.find('div').append(loader); statBar.hide(); // Create target nodes string var tgtNodesStr = ''; var nodes = tgtNodes.split(','); // Loop through each node for (var i in nodes) { // If it is the 1st and only node if (i == 0 && i == nodes.length - 1) { tgtNodesStr += nodes[i]; } // If it is the 1st node of many nodes else if (i == 0 && i != nodes.length - 1) { // Append a comma to the string tgtNodesStr += nodes[i] + ', '; } else { // If it is the last node if (i == nodes.length - 1) { // Append nothing to the string tgtNodesStr += nodes[i]; } else { // Append a comma to the string tgtNodesStr += nodes[i] + ', '; } } } // Create delete form var deleteForm = $('<div class="form"></div>'); deleteForm.append(statBar); deleteForm.append(statBar); // Confirm delete var instr = $('<p>Are you sure you want to delete ' + tgtNodesStr + '?</p>').css('word-wrap', 'break-word'); deleteForm.append(instr); /** * Delete */ var deleteBtn = createButton('Delete'); deleteBtn.click(function() { // Delete the virtual server $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'rmvm', tgt : tgtNodes, args : '', msg : 'out=' + statBarId + ';cmd=rmvm;tgt=' + tgtNodes }, success : updateStatusBar }); // Show status bar loader statBar.show(); // Disable delete button $(this).attr('disabled', 'true'); }); /** * Cancel */ var cancelBtn = createButton('Cancel'); cancelBtn.bind('click', function(){ myTab.remove($(this).parent().parent().attr('id')); }); deleteForm.append(deleteBtn); deleteForm.append(cancelBtn); myTab.add(newTabId, 'Delete', deleteForm, true); myTab.select(newTabId); } /** * Update status bar of a given tab * * @param data Data returned from HTTP request */ function updateStatusBar(data) { // Get ajax response var rsp = data.rsp; var args = data.msg.split(';'); var statBarId = args[0].replace('out=', ''); var cmd = args[1].replace('cmd=', ''); var tgts = args[2].replace('tgt=', '').split(','); if (cmd == 'unlock' || cmd == 'updatenode') { // Hide loader $('#' + statBarId).find('img').hide(); // Write ajax response to status bar var prg = writeRsp(rsp, ''); $('#' + statBarId).find('div').append(prg); } else if (cmd == 'rmvm') { // Get data table var dTable = $('#' + nodesTableId).dataTable(); var failed = false; // Hide loader $('#' + statBarId).find('img').hide(); // Write ajax response to status bar var prg = writeRsp(rsp, ''); $('#' + statBarId).find('div').append(prg); // If there was an error, do not continue if (prg.html().indexOf('Error') > -1) { failed = true; } // Update data table var rowPos; for (var i in tgts) { if (!failed) { // Get row containing the node link and delete it rowPos = findRow(tgts[i], '#' + nodesTableId, 1); dTable.fnDeleteRow(rowPos); } } } else if (cmd == 'xdsh') { // Hide loader $('#' + statBarId).find('img').hide(); // Write ajax response to status bar var prg = $('<pre></pre>'); for (var i in rsp) { for (var j in tgts) { rsp[i] = rsp[i].replace(new RegExp(tgts[j] + ':', 'g'), ''); } prg.append(rsp[i]); prg.append('<br>'); } $('#' + statBarId).find('div').append(prg); // Enable fields $('#' + statBarId).parent().find('input').removeAttr('disabled'); $('#' + statBarId).parent().find('textarea').removeAttr('disabled'); // Enable buttons $('#' + statBarId).parent().find('button').removeAttr('disabled'); } else { // Hide loader $('#' + statBarId).find('img').hide(); // Write ajax response to status bar var prg = writeRsp(rsp, '[A-Za-z0-9._-]+:'); $('#' + statBarId).find('div').append(prg); } } /** * Update power status of a node in the datatable * * @param data Data from HTTP request */ function updatePowerStatus(data) { // Hide power loader var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); powerCol.find('img').hide(); // Get datatable var dTable = $('#' + nodesTableId).dataTable(); // Get xCAT response var rsp = data.rsp; // Loop through each line var node, status, rowPos, strPos; for (var i in rsp) { // Get node name node = rsp[i].split(":")[0]; // If there is no error if (rsp[i].indexOf("Error") < 0 || rsp[i].indexOf("Failed") < 0) { // Get the row containing the node link rowPos = findRow(node, '#' + nodesTableId, 1); // If it was power on, then the data return would contain "Starting" strPos = rsp[i].indexOf("Starting"); if (strPos > -1) { status = 'on'; } else { status = 'off'; } // Update the power status column dTable.fnUpdate(status, rowPos, 3, false); } else { // Power on/off failed alert(rsp[i]); } } // Adjust datatable column size adjustColumnSize(nodesTableId); } /** * Run a script * * @param inst Remote script tab instance */ function runScript(inst) { // Get tab ID var tabId = 'scriptTab' + inst; // Get node name var tgts = $('#' + tabId + ' input[name=target]').val(); // Get script var script = $('#' + tabId + ' textarea').val(); var statBarId = 'scriptStatusBar' + inst; $('#' + statBarId).show(); // Show status bar $('#' + statBarId + ' img').show(); // Show loader $('#' + statBarId + ' p').remove(); // Clear status bar // Disable all fields $('#' + tabId + ' input').attr('disabled', 'true'); $('#' + tabId + ' textarea').attr('disabled', 'true'); // Disable buttons $('#' + tabId + ' button').attr('disabled', 'true'); // Run script $.ajax( { url : 'lib/zCmd.php', dataType : 'json', data : { cmd : 'xdsh', tgt : tgts, args : '-e', att : script, msg : 'out=scriptStatusBar' + inst + ';cmd=xdsh;tgt=' + tgts }, success : updateStatusBar }); } /** * Get an attribute of a given node * * @param node The node * @param attrName The attribute * @return The attribute of the node */ function getNodeAttr(node, attrName) { // Get the row var row = $('[id=' + node + ']').parents('tr'); // Search for the column containing the attribute var attrCol; var cols = row.parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); // Loop through each column for (var i in cols) { // Find column that matches the attribute if (cols.eq(i).html() == attrName) { attrCol = cols.eq(i); break; } } // If the column containing the attribute is found if (attrCol) { // Get the attribute column index var attrIndex = attrCol.index(); // Get the attribute for the given node var attr = row.find('td:eq(' + attrIndex + ')'); return attr.text(); } else { return ''; } } /** * Set a cookie for the OS images * * @param data Data from HTTP request */ function setOSImageCookies(data) { // Get response var rsp = data.rsp; var imageNames = new Array; var profilesHash = new Object(); var osVersHash = new Object(); var osArchsHash = new Object(); var imagePos = 0; var profilePos = 0; var osversPos = 0; var osarchPos = 0; // Get column value var colNameArray = rsp[0].substr(1).split(','); for (var i in colNameArray){ switch (colNameArray[i]){ case 'imagename': imagePos = i; break; case 'profile': profilePos = i; break; case 'osvers': osversPos = i; break; case 'osarch': osarchPos = i; break; default : break; } } // Go through each index for (var i = 1; i < rsp.length; i++) { // Get image name var cols = rsp[i].split(','); var osImage = cols[imagePos].replace(new RegExp('"', 'g'), ''); var profile = cols[profilePos].replace(new RegExp('"', 'g'), ''); var osVer = cols[osversPos].replace(new RegExp('"', 'g'), ''); var osArch = cols[osarchPos].replace(new RegExp('"', 'g'), ''); imageNames.push(osImage); profilesHash[profile] = 1; osVersHash[osVer] = 1; osArchsHash[osArch] = 1; } // Save image names in a cookie $.cookie('imagenames', imageNames); // Save profiles in a cookie var tmp = new Array; for (var key in profilesHash) { tmp.push(key); } $.cookie('profiles', tmp); // Save OS versions in a cookie tmp = new Array; for (var key in osVersHash) { tmp.push(key); } $.cookie('osvers', tmp); // Save OS architectures in a cookie tmp = new Array; for (var key in osArchsHash) { tmp.push(key); } $.cookie('osarchs', tmp); } /** * Set a cookie for the groups * * @param data Data from HTTP request */ function setGroupsCookies(data) { var rsp = data.rsp; $.cookie('groups', rsp); } /** * Find the row index containing a column with a given string * * @param str String to search for * @param table Table to check * @param col Column to find string under * @return The row index containing the search string */ function findRow(str, table, col){ // Get datatable var dTable = $(table).dataTable(); var rows = dTable.fnGetData(); // Loop through each row for (var i in rows) { // If the column contains the search string if (rows[i][col].indexOf(str) > -1) { return i; } } return -1; } /** * Select all checkboxes in the datatable * * @param event Event on element * @param obj Object triggering event */ function selectAllCheckbox(event, obj) { // Get datatable ID // This will ascend from <input> <td> <tr> <thead> <table> var tableObj = obj.parents('.dataTables_scroll').find('.dataTables_scrollBody'); var status = obj.attr('checked'); tableObj.find(' :checkbox').attr('checked', status); event.stopPropagation(); } /** * Load rcons page * * @param tgtNodes Targets to run rcons against */ function loadRconsPage(tgtNodes){ var hostName = window.location.host; var urlPath = window.location.pathname; var redirectUrl = 'https://'; var pos = 0; // We only support one node if (-1 != tgtNodes.indexOf(',')){ alert("You can only open one console at a time!"); return; } redirectUrl += hostName; pos = urlPath.lastIndexOf('/'); redirectUrl += urlPath.substring(0, pos + 1); redirectUrl += 'rcons.php'; // Open the rcons page window.open(redirectUrl + "?rconsnd=" + tgtNodes, '', "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=no,width=590,height=436"); } /** * Create a tool tip for comments * * @param comment Comments to be placed in a tool tip * @return Tool tip */ function createCommentsToolTip(comment) { // Create tooltip container var toolTip = $('<div class="tooltip"></div>'); // Create textarea to hold comment var txtArea = $('<textarea>' + comment + '</textarea>').css({ 'font-size': '10px', 'height': '50px', 'width': '200px', 'background-color': '#000', 'color': '#fff', 'border': '0px', 'display': 'block' }); // Create links to save and cancel changes var lnkStyle = { 'color': '#58ACFA', 'font-size': '10px', 'display': 'inline-block', 'padding': '5px', 'float': 'right' }; var saveLnk = $('<a>Save</a>').css(lnkStyle).hide(); var cancelLnk = $('<a>Cancel</a>').css(lnkStyle).hide(); var infoSpan = $('<span>Click to edit</span>').css(lnkStyle); // Save changes onclick saveLnk.bind('click', function(){ // Get node and comment var node = $(this).parent().parent().find('img').attr('id').replace('Tip', ''); var comments = $(this).parent().find('textarea').val(); // Save comment $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'chdef', tgt : '', args : '-t;node;-o;' + node + ';usercomment=' + comments, msg : 'out=nodesTab;tgt=' + node }, success: showChdefOutput }); // Hide cancel and save links $(this).hide(); cancelLnk.hide(); }); // Cancel changes onclick cancelLnk.bind('click', function(){ // Get original comment and put it back var orignComments = $(this).parent().find('textarea').text(); $(this).parent().find('textarea').val(orignComments); // Hide cancel and save links $(this).hide(); saveLnk.hide(); infoSpan.show(); }); // Show save link when comment is edited txtArea.bind('click', function(){ saveLnk.show(); cancelLnk.show(); infoSpan.hide(); }); toolTip.append(txtArea); toolTip.append(cancelLnk); toolTip.append(saveLnk); toolTip.append(infoSpan); return toolTip; } /** * Create a tool tip for node status * * @return Tool tip */ function createStatusToolTip() { // Create tooltip container var toolTip = $('<div class="tooltip"></div>').css({ 'width': '150px', 'font-weight': 'normal' }); // Create info text var info = $('<p></p>').css({ 'white-space': 'normal' }); info.append('Click here to refresh the node status. To configure the xCAT monitor, '); // Create link to turn on xCAT monitoring var monitorLnk = $('<a>click here</a>').css({ 'color': '#58ACFA', 'font-size': '10px' }); // Open dialog to configure xCAT monitor monitorLnk.bind('click', function(){ // Check if xCAT monitor is enabled $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'monls', tgt : '', args : 'xcatmon', msg : '' }, success : openConfXcatMon }); }); info.append(monitorLnk); toolTip.append(info); return toolTip; } /** * Create a tool tip for power status * * @return Tool tip */ function createPowerToolTip() { // Create tooltip container var toolTip = $('<div class="tooltip">Click here to refresh the power status</div>').css({ 'width': '150px', 'white-space': 'normal', 'font-weight': 'normal' }); return toolTip; } /** * Create a tool tip for monitoring status * * @return Tool tip */ function createMonitorToolTip() { // Create tooltip container var toolTip = $('<div class="tooltip">Click here to refresh the monitoring status</div>').css({ 'width': '150px', 'white-space': 'normal', 'font-weight': 'normal' }); return toolTip; } /** * Open dialog to configure xCAT monitor * * @param data Data returned from HTTP request */ function openConfXcatMon(data) { // Create info bar var info = createInfoBar('Configure the xCAT monitor. Select to enable or disable the monitor below.'); var dialog = $('<div></div>'); dialog.append(info); // Create status area var statusArea = $('<div></div>').css('padding-top', '10px'); var label = $('<label>Status:</label>'); statusArea.append(label); // Get xCAT monitor status var status = data.rsp[0]; var buttons; // If xCAT monitor is disabled if (status.indexOf('not-monitored') > -1) { status = $('<span>Disabled</span>').css('padding', '0px 5px'); statusArea.append(status); // Create enable and cancel buttons buttons = { "Enable": function(){ // Enable xCAT monitor $.ajax({ url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'monstart', tgt : '', args : 'xcatmon', msg : '' }, success : function(data){ openDialog('info', data.rsp[0]); } }); $(this).dialog("close"); }, "Cancel": function(){ $(this).dialog("close"); } }; } else { status = $('<span>Enabled</span>').css('padding', '0px 5px'); statusArea.append(status); // Create disable and cancel buttons buttons = { "Disable": function(){ // Disable xCAT monitor $.ajax({ url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'monstop', tgt : '', args : 'xcatmon', msg : '' }, success : function(data){ openDialog('info', data.rsp[0]); } }); $(this).dialog("close"); }, "Cancel": function(){ $(this).dialog("close"); } }; } dialog.append(statusArea); // Open dialog dialog.dialog({ modal: true, width: 500, buttons: buttons }); } /** * Show chdef output * * @param data Data returned from HTTP request */ function showChdefOutput(data) { // Get output var out = data.rsp; var args = data.msg.split(';'); var tabID = args[0].replace('out=', ''); var tgt = args[1].replace('tgt=', ''); // Find info bar on nodes tab, if any var info = $('#' + tabID).find('.ui-state-highlight'); if (!info.length) { // Create info bar if one does not exist info = createInfoBar(''); $('#' + tabID).append(info); } // Go through output and append to paragraph var prg = $('<p></p>'); for (var i in out) { prg.append(tgt + ': ' + out[i] + '<br>'); } info.append(prg); } /** * Set node attributes * * @param data Data returned from HTTP request */ function setNodeAttrs(data) { // Clear hash table containing definable node attributes nodeAttrs = new Array(); // Get definable attributes var attrs = data.rsp[2].split(/\n/); // Go through each line var attr, key, descr; for (var i in attrs) { attr = attrs[i]; // If the line is not empty if (attr) { // If the line has the attribute name if (attr.indexOf(':') && attr.indexOf(' ')) { // Get attribute name and description key = jQuery.trim(attr.substring(0, attr.indexOf(':'))); descr = jQuery.trim(attr.substring(attr.indexOf(':') + 1)); // Remove arrow brackets descr = descr.replace(new RegExp('<|>', 'g'), ''); // Set hash table where key = attribute name and value = description nodeAttrs[key] = descr; } else { // Remove arrow brackets attr = attr.replace(new RegExp('<|>', 'g'), ''); // Append description to hash table nodeAttrs[key] = nodeAttrs[key] + '\n' + attr; } } // End of if } // End of for } /** * Load set node properties page * * @param tgtNode Target node to set properties */ function editNodeProps(tgtNode) { // Get nodes tab var tab = getNodesTab(); // Generate new tab ID var inst = 0; var newTabId = 'editPropsTab' + inst; while ($('#' + newTabId).length) { // If one already exists, generate another one inst = inst + 1; newTabId = 'editPropsTab' + inst; } // Open new tab // Create set properties form var editPropsForm = $('<div class="form"></div>'); // Create info bar var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); editPropsForm.append(infoBar); // Create an input for each definable attribute var div, label, input, descr, value; // Set node attribute origAttrs[tgtNode]['node'] = tgtNode; for (var key in nodeAttrs) { // If an attribute value exists if (origAttrs[tgtNode][key]) { // Set the value value = origAttrs[tgtNode][key]; } else { value = ''; } // Create label and input for attribute div = $('<div></div>').css('display', 'inline-table'); label = $('<label>' + key + ':</label>').css('vertical-align', 'middle'); input = $('<input type="text" value="' + value + '" title="' + nodeAttrs[key] + '"/>').css('margin-top', '5px'); // Change border to blue onchange input.bind('change', function(event) { $(this).css('border-color', 'blue'); }); div.append(label); div.append(input); editPropsForm.append(div); } // Change style for last division div.css({ 'display': 'block', 'margin': '0px 0px 10px 0px' }); // Generate tooltips editPropsForm.find('div input[title]').tooltip({ position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.8, delay: 0, predelay: 800, events: { def: "mouseover,mouseout", input: "mouseover,mouseout", widget: "focus mouseover,blur mouseout", tooltip: "mouseover,mouseout" } }); // Save changes var saveBtn = createButton('Save'); saveBtn.click(function() { // Get all inputs var inputs = $('#' + newTabId + ' input'); // Go through each input var args = ''; var attrName, attrVal; inputs.each(function(){ // If the border color is blue if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { // Change border color back to normal $(this).css('border-color', ''); // Get attribute name and value attrName = $(this).parent().find('label').text().replace(':', ''); attrVal = $(this).val(); // Build argument string if (args) { // Handle subsequent arguments args += ';' + attrName + '=' + attrVal; } else { // Handle the 1st argument args += attrName + '=' + attrVal; } } }); // Send command to change node attributes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'chdef', tgt : '', args : '-t;node;-o;' + tgtNode + ';' + args, msg : 'out=' + newTabId + ';tgt=' + tgtNode }, success: showChdefOutput }); }); editPropsForm.append(saveBtn); // Cancel changes var cancelBtn = createButton('Cancel'); cancelBtn.click(function() { // Close the tab tab.remove($(this).parent().parent().attr('id')); }); editPropsForm.append(cancelBtn); // Append to discover tab tab.add(newTabId, 'Edit', editPropsForm, true); // Select new tab tab.select(newTabId); } /** * Open set node attributes dialog */ function openSetAttrsDialog() { // Open new tab // Create set properties form var setPropsForm = $('<div class="form"></div>'); // Create info bar var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); setPropsForm.append(infoBar); // Create an input for each definable attribute var div, label, input, descr, value; for (var key in nodeAttrs) { value = ''; // Create label and input for attribute div = $('<div></div>').css('display', 'inline'); label = $('<label>' + key + ':</label>').css('vertical-align', 'middle'); input = $('<input type="text" value="' + value + '" title="' + nodeAttrs[key] + '"/>').css('margin-top', '5px'); // Change border to blue onchange input.bind('change', function(event) { $(this).css('border-color', 'blue'); }); div.append(label); div.append(input); setPropsForm.append(div); } // Change style for last division div.css({ 'display': 'block', 'margin': '0px 0px 10px 0px' }); // Generate tooltips setPropsForm.find('div input[title]').tooltip({ position: "center right", offset: [-2, 10], effect: "fade", opacity: 0.8, delay: 0, predelay: 800, events: { def: "mouseover,mouseout", input: "mouseover,mouseout", widget: "focus mouseover,blur mouseout", tooltip: "mouseover,mouseout" }, // Change z index to show tooltip in front onBeforeShow: function() { this.getTip().css('z-index', $.topZIndex()); } }); // Enable vertical scroll setPropsForm.css('overflow', 'auto'); // Open form as a dialog setPropsForm.dialog({ title: 'Set attributes', modal: true, close: function(){ $(this).remove(); }, height: 400, width: 700, buttons: { "Save": function() { // Remove any warning messages $(this).find('.ui-state-error').remove(); // Get all inputs var inputs = $(this).find('input'); // Go through each input var args = ''; var tgtNode, attrName, attrVal; inputs.each(function(){ // If the border color is blue if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { // Change border color back to normal $(this).css('border-color', ''); // Get attribute name and value attrName = $(this).parent().find('label').text().replace(':', ''); attrVal = $(this).val(); // Get node name if (attrName == 'node') { tgtNode = attrVal; } else { // Build argument string if (args) { // Handle subsequent arguments args += ';' + attrName + '=' + attrVal; } else { // Handle the 1st argument args += attrName + '=' + attrVal; } } } }); // Send command to change node attributes $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'chdef', tgt : '', args : '-t;node;-o;' + tgtNode + ';' + args, msg : 'node=' + tgtNode }, /** * Show results * * @param data * Data returned from HTTP request * @return Nothing */ success: function(data) { // Get output var out = data.rsp; var node = data.msg.replace('node=', ''); // Go through output and append to paragraph var msg = ''; for (var i in out) { if (!msg) { msg = node + ': ' + out[i]; } else { msg += '<br>' + node + ': ' + out[i]; } } openDialog('info', msg); } }); // Close dialog $(this).dialog( "close" ); }, "Cancel": function(){ $(this).dialog( "close" ); } } }); } /** * Turn on monitoring for a given node * * @param node Node to monitor on or off * @param monitor Monitor state, on or off */ function monitorNode(node, monitor) { // Show ganglia loader var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); gangliaCol.find('img').show(); if (monitor == 'on') { // Append loader to warning bar var warningBar = $('#nodesTab').find('.ui-state-error p'); if (warningBar.length) { warningBar.append(createLoader('')); } if (node) { // Check if ganglia RPMs are installed $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'gangliacheck;' + node, msg : node // Node range will be passed along in data.msg }, /** * Start ganglia on a given node range * * @param data Data returned from HTTP request */ success : function(data) { // Get response var out = data.rsp[0].split(/\n/); // Go through each line var warn = false; var warningMsg = ''; for (var i in out) { // If an RPM is not installed if (out[i].indexOf('not installed') > -1) { warn = true; if (warningMsg) { warningMsg += '<br>' + out[i]; } else { warningMsg = out[i]; } } } // If there are warnings if (warn) { // Create warning bar var warningBar = createWarnBar(warningMsg); warningBar.css('margin-bottom', '10px'); warningBar.prependTo($('#nodesTab')); } else { $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'gangliastart;' + data.msg + ';-r', msg : data.msg }, success : function(data) { // Remove any warnings $('#nodesTab').find('.ui-state-error').remove(); // Update datatable $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'gangliastatus;' + data.msg, msg : '' }, success : loadGangliaStatus }); } }); } // End of if (warn) } // End of function(data) }); } else { $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : 'gangliastart', msg : '' }, success : function(data) { // Remove any warnings $('#nodesTab').find('.ui-state-error').remove(); } }); } // End of if (node) } else { var args; if (node) { args = 'gangliastop;' + node + ';-r'; } else { args = 'gangliastop'; } $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'webrun', tgt : '', args : args, msg : '' }, success : function(data) { // Hide ganglia loader var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); gangliaCol.find('img').hide(); } }); } } /** * Install Ganglia on a given node * * @param node Node to install Ganglia on */ function installGanglia(node) { var iframe = createIFrame('lib/cmd.php?cmd=webrun&tgt=&args=installganglia;' + node + '&msg=' + node + '&opts=flush'); iframe.prependTo($('#nodesTab')); // Turn on Ganglia for node monitorNode(node, 'on'); } /** * After nodes are loaded, load more information based on different hardware architectures * * @param group Group name */ function advancedLoad(group){ var tempIndex = 0; var tableHeaders = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); var colNameHash = new Object(); var colName = ''; var archCol = 0, hcpCol = 0; // Find out the column name and their index for (tempIndex = 0; tempIndex < tableHeaders.size(); tempIndex++){ var header = tableHeaders.eq(tempIndex); // Skip headers that are links, e.g. status, power, and monitor if (header.find('a').size() > 0){ continue; } colName = header.text(); if (colName) { colNameHash[colName] = tempIndex; } } // If there is no arch column, exit because you cannot distinguish hardware type if (!colNameHash['arch']) { return; } if (!colNameHash['hcp']) { return; } archCol = colNameHash['arch']; hcpCol = colNameHash['hcp']; // Get hardware control point var rows = $('#' + nodesTableId + ' tbody tr'); var hcps = new Object(); var rowsNum = rows.size(); for (var j = 0; j < rowsNum; j++) { var val = rows.eq(j).find('td').eq(hcpCol).html(); var archval = rows.eq(j).find('td').eq(archCol).html(); if (-1 == archval.indexOf('390')){ continue; } hcps[val] = 1; } var args; for (var h in hcps) { // Get node without domain name args = h.split('.'); // If there are no disk pools or network names cookie for this hcp if (!$.cookie(args[0] + 'diskpools') || !$.cookie(args[0] + 'networks')) { // Check if SMAPI is online $.ajax( { url : 'lib/cmd.php', dataType : 'json', data : { cmd : 'lsvm', tgt : args[0], args : '', msg : 'group=' + group + ';hcp=' + args[0] }, // Load hardware control point specific info // Get disk pools and network names success : loadHcpInfo }); } } // End of for } /** * Jump to provision page on-click * * @param tgtNodes Target nodes */ function jump2Provision(tgtNodes){ var nodeArray = tgtNodes.split(','); var nodeName = ''; var index = 0; var archType = ''; var errorMsg = ''; var master = ''; var tftpserver = ''; var nfsserver = ''; var diaDiv = $('<div title="Provision" class="form" id="deployDiv"></div>'); // Check the first node's arch type for (index in nodeArray){ nodeName = nodeArray[index]; // Skip if node does not have arch if (!origAttrs[nodeName]['arch']){ errorMsg = 'Nodes should have arch defined! '; break; } if (index == 0) { archType = origAttrs[nodeName]['arch']; } // Skip if nodes do not have same arch if (archType != origAttrs[nodeName]['arch']){ errorMsg = 'Nodes should belong to the same arch!<br/>'; break; } } // Skip if nodes do not have MAC address for (index in nodeArray){ if (!origAttrs[nodeName]['mac'] || !origAttrs[nodeName]['ip']){ errorMsg += 'Nodes should have the IP and MAC addresses defined!<br/>'; break; } } if (archType.indexOf('390') != -1) { errorMsg += 'Please use the provision page'; } // Open dialog to show error message if (errorMsg){ diaDiv.append(createWarnBar(errorMsg)); diaDiv.dialog({ modal: true, close: function(){ $(this).remove(); }, width: 400, buttons: { 'Close': function(){ $(this).dialog('destroy'); } } }); return; } if (origAttrs[nodeName]['xcatmaster']) { master = origAttrs[nodeName]['xcatmaster']; } if (origAttrs[nodeName]['tftpserver']) { tftpserver = origAttrs[nodeName]['tftpserver']; } if (origAttrs[nodeName]['nfsserver']) { nfsserver = origAttrs[nodeName]['nfsserver']; } window.location.href = 'provision.php?nodes=' + tgtNodes + '&arch=' + archType + '&master=' + master + '&tftpserver=' + tftpserver + '&nfsserver=' + nfsserver; }