0edc77694f
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/branches/2.8@16700 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
2171 lines
64 KiB
JavaScript
2171 lines
64 KiB
JavaScript
/**
|
|
* Global variables
|
|
*/
|
|
var serviceTabs;
|
|
var nodeName;
|
|
var nodePath;
|
|
var nodeStatus;
|
|
var gangliaTimer;
|
|
|
|
/**
|
|
* Initialize service page
|
|
*/
|
|
function initServicePage() {
|
|
// Load theme
|
|
var theme = $.cookie('xcat_theme');
|
|
if (theme) {
|
|
switch (theme) {
|
|
case 'cupertino':
|
|
includeCss("css/themes/jquery-ui-cupertino.css");
|
|
break;
|
|
case 'dark_hive':
|
|
includeCss("css/themes/jquery-ui-dark_hive.css");
|
|
break;
|
|
case 'redmond':
|
|
includeCss("css/themes/jquery-ui-redmond.css");
|
|
break;
|
|
case 'start':
|
|
includeCss("css/themes/jquery-ui-start.css");
|
|
break;
|
|
case 'sunny':
|
|
includeCss("css/themes/jquery-ui-sunny.css");
|
|
break;
|
|
case 'ui_dark':
|
|
includeCss("css/themes/jquery-ui-ui_darkness.css");
|
|
break;
|
|
default:
|
|
includeCss("css/themes/jquery-ui-start.css");
|
|
}
|
|
} else {
|
|
includeCss("css/themes/jquery-ui-start.css");
|
|
}
|
|
|
|
// Load jQuery stylesheets
|
|
includeCss("css/jquery.dataTables.css");
|
|
includeCss("css/superfish.css");
|
|
includeCss("css/jstree.css");
|
|
includeCss("css/jquery.jqplot.css");
|
|
|
|
// Load custom stylesheet
|
|
includeCss("css/style.css");
|
|
|
|
// Reuqired JQuery plugins
|
|
includeJs("js/jquery/jquery.dataTables.min.js");
|
|
includeJs("js/jquery/jquery.cookie.min.js");
|
|
includeJs("js/jquery/tooltip.min.js");
|
|
includeJs("js/jquery/superfish.min.js");
|
|
includeJs("js/jquery/jquery.jqplot.min.js");
|
|
includeJs("js/jquery/jqplot.dateAxisRenderer.min.js");
|
|
|
|
// Custom plugins
|
|
includeJs("js/custom/esx.js");
|
|
includeJs("js/custom/kvm.js");
|
|
includeJs("js/custom/zvm.js");
|
|
|
|
// Enable settings link
|
|
$('#xcat_settings').click(function() {
|
|
openSettings();
|
|
});
|
|
|
|
// Show service page
|
|
$("#content").children().remove();
|
|
includeJs("js/service/utils.js");
|
|
loadServicePage();
|
|
|
|
// Initialize tab index history
|
|
$.cookie('tabindex_history', '0,0');
|
|
}
|
|
|
|
/**
|
|
* Load service page
|
|
*/
|
|
function loadServicePage() {
|
|
// If the page is loaded
|
|
if ($('#content').children().length) {
|
|
// Do not load again
|
|
return;
|
|
}
|
|
|
|
// Create manage and provision tabs
|
|
serviceTabs = new Tab();
|
|
serviceTabs.init();
|
|
$('#content').append(serviceTabs.object());
|
|
|
|
var manageTabId = 'manageTab';
|
|
serviceTabs.add(manageTabId, 'Manage', '', false);
|
|
|
|
// Get nodes owned by user
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'tabdump',
|
|
tgt : '',
|
|
args : 'nodetype',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
setUserNodes(data);
|
|
setMaxVM();
|
|
getUserNodesDef();
|
|
getNodesCurrentLoad();
|
|
loadManagePage(manageTabId);
|
|
}
|
|
});
|
|
|
|
// Get OS image names
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
async : true,
|
|
data : {
|
|
cmd : 'tabdump',
|
|
tgt : '',
|
|
args : 'osimage',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
setOSImageCookies(data);
|
|
}
|
|
});
|
|
|
|
// Get contents of hosts table
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
async : true,
|
|
data : {
|
|
cmd : 'tabdump',
|
|
tgt : '',
|
|
args : 'hosts',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
setGroupCookies(data);
|
|
}
|
|
});
|
|
|
|
var provTabId = 'provisionTab';
|
|
serviceTabs.add(provTabId, 'Provision', '', false);
|
|
loadServiceProvisionPage(provTabId);
|
|
|
|
serviceTabs.select(manageTabId);
|
|
}
|
|
|
|
/**
|
|
* Load the service portal's provision page
|
|
*
|
|
* @param tabId Tab ID where page will reside
|
|
*/
|
|
function loadServiceProvisionPage(tabId) {
|
|
// Create info bar
|
|
var infoBar = createInfoBar('Select a platform to provision a node on, then click Ok.');
|
|
|
|
// Create provision page
|
|
var provPg = $('<div class="form"></div>');
|
|
$('#' + tabId).append(infoBar, provPg);
|
|
|
|
// Create radio buttons for platforms
|
|
var hwList = $('<ol>Platforms available:</ol>');
|
|
var esx = $('<li><input type="radio" name="hw" value="esx" checked/>ESX</li>');
|
|
var kvm = $('<li><input type="radio" name="hw" value="kvm"/>KVM</li>');
|
|
var zvm = $('<li><input type="radio" name="hw" value="zvm"/>z\/VM</li>');
|
|
|
|
hwList.append(esx);
|
|
hwList.append(kvm);
|
|
hwList.append(zvm);
|
|
provPg.append(hwList);
|
|
|
|
/**
|
|
* Ok
|
|
*/
|
|
var okBtn = createButton('Ok');
|
|
okBtn.bind('click', function(event) {
|
|
var userName = $.cookie('xcat_username');
|
|
var tmp = $.cookie(userName + '_usrnodes');
|
|
|
|
// Get maximun number for nodes from cookie
|
|
var nodes = '';
|
|
var maxVM = 0;
|
|
if (tmp.length) {
|
|
nodes = tmp.split(',');
|
|
maxVM = parseInt($.cookie(userName + '_maxvm'));
|
|
|
|
// Do not allow user to clone if the maximum number of VMs is reached
|
|
if (nodes.length >= maxVM) {
|
|
var warn = createWarnBar('You have reached the maximum number of virtual machines allowed (' + maxVM + '). Delete unused virtual machines or contact your system administrator request more virtual machines.');
|
|
warn.prependTo($('#' + tabId));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get hardware that was selected
|
|
var hw = $(this).parent().find('input[name="hw"]:checked').val();
|
|
var newTabId = hw + 'ProvisionTab';
|
|
|
|
if ($('#' + newTabId).size() > 0){
|
|
serviceTabs.select(newTabId);
|
|
} else {
|
|
var title = '';
|
|
|
|
// Create an instance of the plugin
|
|
var plugin = null;
|
|
switch (hw) {
|
|
case "kvm":
|
|
plugin = new kvmPlugin();
|
|
title = 'KVM';
|
|
break;
|
|
case "esx":
|
|
plugin = new esxPlugin();
|
|
title = 'ESX';
|
|
break;
|
|
case "blade":
|
|
plugin = new bladePlugin();
|
|
title = 'BladeCenter';
|
|
break;
|
|
case "hmc":
|
|
plugin = new hmcPlugin();
|
|
title = 'System p';
|
|
break;
|
|
case "ipmi":
|
|
plugin = new ipmiPlugin();
|
|
title = 'iDataPlex';
|
|
break;
|
|
case "zvm":
|
|
plugin = new zvmPlugin();
|
|
title = 'z/VM';
|
|
|
|
// Get zVM host names
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
async : false,
|
|
data : {
|
|
cmd : 'webportal',
|
|
tgt : '',
|
|
args : 'lszvm',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
setzVMCookies(data);
|
|
}
|
|
});
|
|
|
|
// Get master copies for clone
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
async : false,
|
|
data : {
|
|
cmd : 'webportal',
|
|
tgt : '',
|
|
args : 'lsgoldenimages',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
setGoldenImagesCookies(data);
|
|
}
|
|
});
|
|
|
|
break;
|
|
}
|
|
|
|
// Select tab
|
|
serviceTabs.add(newTabId, title, '', true);
|
|
serviceTabs.select(newTabId);
|
|
plugin.loadServiceProvisionPage(newTabId);
|
|
}
|
|
});
|
|
provPg.append(okBtn);
|
|
}
|
|
|
|
/**
|
|
* Load manage page
|
|
*
|
|
* @param tabId Tab ID where page will reside
|
|
*/
|
|
function loadManagePage(tabId) {
|
|
// Create manage form
|
|
var manageForm = $('<div></div>');
|
|
|
|
// Append to manage tab
|
|
$('#' + tabId).append(manageForm);
|
|
}
|
|
|
|
/**
|
|
* Get the user nodes definitions
|
|
*/
|
|
function getUserNodesDef() {
|
|
var userName = $.cookie('xcat_username');
|
|
var userNodes = $.cookie(userName + '_usrnodes');
|
|
if (userNodes) {
|
|
// Get nodes definitions
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'lsdef',
|
|
tgt : '',
|
|
args : userNodes,
|
|
msg : ''
|
|
},
|
|
|
|
success : loadNodesTable
|
|
});
|
|
} else {
|
|
// Clear the tab before inserting the table
|
|
$('#manageTab').append(createWarnBar('No nodes were found belonging to you!'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load user nodes definitions into a table
|
|
*
|
|
* @param data Data from HTTP request
|
|
*/
|
|
function loadNodesTable(data) {
|
|
// Clear the tab before inserting the table
|
|
$('#manageTab').children().remove();
|
|
|
|
// Nodes datatable ID
|
|
var nodesDTId = 'userNodesDT';
|
|
|
|
// Hash of node attributes
|
|
var attrs = new Object();
|
|
// Node attributes
|
|
var headers = new Object();
|
|
var node = null, args;
|
|
// Create hash of node attributes
|
|
for (var i in data.rsp) {
|
|
// Get node name
|
|
if (data.rsp[i].indexOf('Object name:') > -1) {
|
|
var temp = data.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 = data.rsp[i].split('=', 2);
|
|
var key = jQuery.trim(args[0]);
|
|
var val = jQuery.trim(data.rsp[i].substring(data.rsp[i].indexOf('=') + 1, data.rsp[i].length));
|
|
|
|
// Create a hash table
|
|
attrs[node][key] = val;
|
|
headers[key] = 1;
|
|
}
|
|
|
|
// Sort headers
|
|
var sorted = new Array();
|
|
var attrs2show = new Array('arch', 'groups', 'hcp', 'hostnames', 'ip', 'os', 'userid', 'mgt');
|
|
for (var key in headers) {
|
|
// Show node attributes
|
|
if (jQuery.inArray(key, attrs2show) > -1) {
|
|
sorted.push(key);
|
|
}
|
|
}
|
|
sorted.sort();
|
|
|
|
// Add column for check box, node, ping, power, monitor, and comments
|
|
sorted.unshift('<input type="checkbox" onclick="selectAll(event, $(this))">',
|
|
'node',
|
|
'<span><a>status</a></span><img src="images/loader.gif" style="display: none;"></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 nodesDT = new DataTable(nodesDTId);
|
|
nodesDT.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('statustime') < 0) {
|
|
var val = attrs[node][key];
|
|
if (val) {
|
|
row.push($('<span>' + val + '</span>'));
|
|
} else {
|
|
row.push('');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the row to the table
|
|
nodesDT.add(row);
|
|
}
|
|
|
|
// Create info bar
|
|
var infoBar = createInfoBar('Manage and monitor your virtual machines.');
|
|
$('#manageTab').append(infoBar);
|
|
|
|
// Insert action bar and nodes datatable
|
|
$('#manageTab').append(nodesDT.object());
|
|
|
|
// Turn table into a datatable
|
|
$('#' + nodesDTId).dataTable({
|
|
'iDisplayLength': 50,
|
|
'bLengthChange': false,
|
|
"bScrollCollapse": true,
|
|
"sScrollY": "400px",
|
|
"sScrollX": "110%",
|
|
"bAutoWidth": true,
|
|
"oLanguage": {
|
|
"oPaginate": {
|
|
"sNext": "",
|
|
"sPrevious": ""
|
|
}
|
|
}
|
|
});
|
|
|
|
// Set datatable header class to add color
|
|
// $('.datatable thead').attr('class', 'ui-widget-header');
|
|
|
|
// Do not sort ping, power, and comment column
|
|
$('#' + nodesDTId + ' thead tr th').click(function() {
|
|
getNodeAttrs(group);
|
|
});
|
|
var checkboxCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(0)');
|
|
var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
|
|
var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
|
|
var monitorCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
|
|
var commentCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(5)');
|
|
checkboxCol.unbind('click');
|
|
pingCol.unbind('click');
|
|
powerCol.unbind('click');
|
|
monitorCol.unbind('click');
|
|
commentCol.unbind('click');
|
|
|
|
// Refresh the node ping, power, and monitor status on-click
|
|
var nodes = getNodesShown(nodesDTId);
|
|
pingCol.find('span a').click(function() {
|
|
refreshNodeStatus(nodes);
|
|
});
|
|
powerCol.find('span a').click(function() {
|
|
refreshPowerStatus(nodes);
|
|
});
|
|
monitorCol.find('span a').click(function() {
|
|
refreshGangliaStatus(nodes);
|
|
});
|
|
|
|
// Create actions menu
|
|
// Power on
|
|
var powerOnLnk = $('<a>Power on</a>');
|
|
powerOnLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
powerNode(tgtNodes, 'on');
|
|
}
|
|
});
|
|
|
|
// Power off
|
|
var powerOffLnk = $('<a>Power off</a>');
|
|
powerOffLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
powerNode(tgtNodes, 'off');
|
|
}
|
|
});
|
|
|
|
// Power softoff
|
|
var powerSoftoffLnk = $('<a>Shutdown</a>');
|
|
powerSoftoffLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
powerNode(tgtNodes, 'softoff');
|
|
}
|
|
});
|
|
|
|
// Turn monitoring on
|
|
var monitorOnLnk = $('<a>Monitor on</a>');
|
|
monitorOnLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
monitorNode(tgtNodes, 'on');
|
|
}
|
|
});
|
|
|
|
// Turn monitoring off
|
|
var monitorOffLnk = $('<a>Monitor off</a>');
|
|
monitorOffLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
monitorNode(tgtNodes, 'off');
|
|
}
|
|
});
|
|
|
|
// Clone
|
|
var cloneLnk = $('<a>Clone</a>');
|
|
cloneLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
cloneNode(tgtNodes);
|
|
}
|
|
});
|
|
|
|
// Delete
|
|
var deleteLnk = $('<a>Delete</a>');
|
|
deleteLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
deleteNode(tgtNodes);
|
|
}
|
|
});
|
|
|
|
// Unlock
|
|
var unlockLnk = $('<a>Unlock</a>');
|
|
unlockLnk.click(function() {
|
|
var tgtNodes = getNodesChecked(nodesDTId);
|
|
if (tgtNodes) {
|
|
unlockNode(tgtNodes);
|
|
}
|
|
});
|
|
|
|
// Create action bar
|
|
var actionBar = $('<div class="actionBar"></div>').css('width', '370px');
|
|
|
|
// Prepend menu to datatable
|
|
var actionsLnk = $('<a>Actions</a>');
|
|
var refreshLnk = $('<a>Refresh</a>');
|
|
refreshLnk.click(function() {
|
|
// Get nodes owned by user
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'tabdump',
|
|
tgt : '',
|
|
args : 'nodetype',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
// Save nodes owned by user
|
|
setUserNodes(data);
|
|
getNodesCurrentLoad();
|
|
|
|
// Refresh nodes table
|
|
var userName = $.cookie('xcat_username');
|
|
var userNodes = $.cookie(userName + '_usrnodes');
|
|
if (userNodes) {
|
|
// Get nodes definitions
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'lsdef',
|
|
tgt : '',
|
|
args : userNodes,
|
|
msg : ''
|
|
},
|
|
|
|
success : loadNodesTable
|
|
});
|
|
} else {
|
|
// Clear the tab before inserting the table
|
|
$('#manageTab').children().remove();
|
|
$('#manageTab').append(createWarnBar('You are not managing any node. Try to provision a node.'));
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
var actionMenu = createMenu([cloneLnk, deleteLnk, monitorOnLnk, monitorOffLnk, powerOnLnk, powerOffLnk, powerSoftoffLnk, unlockLnk]);
|
|
var menu = createMenu([[actionsLnk, actionMenu], refreshLnk]);
|
|
menu.superfish();
|
|
actionBar.append(menu);
|
|
|
|
// Set correct theme for action menu
|
|
actionMenu.find('li').hover(function() {
|
|
setMenu2Theme($(this));
|
|
}, function() {
|
|
setMenu2Normal($(this));
|
|
});
|
|
|
|
// Create a division to hold actions menu
|
|
var menuDiv = $('<div id="' + nodesDTId + '_menuDiv" class="menuDiv"></div>');
|
|
$('#' + nodesDTId + '_wrapper').prepend(menuDiv);
|
|
menuDiv.append(actionBar);
|
|
$('#' + nodesDTId + '_filter').appendTo(menuDiv);
|
|
|
|
// Get power and monitor status
|
|
var nodes = getNodesShown(nodesDTId);
|
|
refreshPowerStatus(nodes);
|
|
refreshGangliaStatus(nodes);
|
|
}
|
|
|
|
/**
|
|
* Refresh ping status for each node
|
|
*
|
|
* @param nodes Nodes to get ping status
|
|
*/
|
|
function refreshNodeStatus(nodes) {
|
|
// Show ping loader
|
|
var nodesDTId = 'userNodesDT';
|
|
var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
|
|
pingCol.find('img').show();
|
|
|
|
// Get the node ping status
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'nodestat',
|
|
tgt : nodes,
|
|
args : '-u',
|
|
msg : ''
|
|
},
|
|
|
|
success : loadNodePing
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Load node ping status for each node
|
|
*
|
|
* @param data Data returned from HTTP request
|
|
*/
|
|
function loadNodePing(data) {
|
|
var nodesDTId = 'userNodesDT';
|
|
var datatable = $('#' + nodesDTId).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, '#' + nodesDTId, 1);
|
|
|
|
// Update ping status column
|
|
datatable.fnUpdate(status, rowPos, 2, false);
|
|
}
|
|
|
|
// Hide status loader
|
|
var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)');
|
|
pingCol.find('img').hide();
|
|
adjustColumnSize(nodesDTId);
|
|
}
|
|
|
|
/**
|
|
* Refresh power status for each node
|
|
*
|
|
* @param nodes Nodes to get power status
|
|
*/
|
|
function refreshPowerStatus(nodes) {
|
|
// Show power loader
|
|
var nodesDTId = 'userNodesDT';
|
|
var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
|
|
powerCol.find('img').show();
|
|
|
|
// Get power status
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'rpower',
|
|
tgt : nodes,
|
|
args : 'stat',
|
|
msg : ''
|
|
},
|
|
|
|
success : loadPowerStatus
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Load power status for each node
|
|
*
|
|
* @param data Data returned from HTTP request
|
|
*/
|
|
function loadPowerStatus(data) {
|
|
var nodesDTId = 'userNodesDT';
|
|
var datatable = $('#' + nodesDTId).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, '#' + nodesDTId, 1);
|
|
|
|
// Update the power status column
|
|
datatable.fnUpdate(status, rowPos, 3, false);
|
|
}
|
|
|
|
// Hide power loader
|
|
var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
|
|
powerCol.find('img').hide();
|
|
adjustColumnSize(nodesDTId);
|
|
}
|
|
|
|
/**
|
|
* Refresh the status of Ganglia for each node
|
|
*
|
|
* @param nodes Nodes to get Ganglia status
|
|
*/
|
|
function refreshGangliaStatus(nodes) {
|
|
// Show ganglia loader
|
|
var nodesDTId = 'userNodesDT';
|
|
var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
|
|
gangliaCol.find('img').show();
|
|
|
|
// Get the status of Ganglia
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : 'gangliastatus;' + nodes,
|
|
msg : ''
|
|
},
|
|
|
|
success : loadGangliaStatus
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Load the status of Ganglia for a given group
|
|
*
|
|
* @param data Data returned from HTTP request
|
|
*/
|
|
function loadGangliaStatus(data) {
|
|
// Get datatable
|
|
var nodesDTId = 'userNodesDT';
|
|
var datatable = $('#' + nodesDTId).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, '#' + nodesDTId, 1);
|
|
|
|
// Update the power status column
|
|
datatable.fnUpdate(status, rowNum, 4);
|
|
}
|
|
}
|
|
|
|
// Hide Ganglia loader
|
|
var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
|
|
gangliaCol.find('img').hide();
|
|
adjustColumnSize(nodesDTId);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
// Create a new tab to show inventory
|
|
var tabId = node + '_inventory';
|
|
|
|
if(!$('#' + tabId).length) {
|
|
// Add new tab, only if one does not exist
|
|
var loader = createLoader(node + 'Loader');
|
|
loader = $('<center></center>').append(loader);
|
|
serviceTabs.add(tabId, node, loader, true);
|
|
|
|
// Get node inventory
|
|
var msg = 'out=' + tabId + ',node=' + node;
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'rinv',
|
|
tgt : node,
|
|
args : 'all',
|
|
msg : msg
|
|
},
|
|
|
|
success : function(data) {
|
|
var args = data.msg.split(',');
|
|
|
|
// Get node
|
|
var node = args[1].replace('node=', '');
|
|
|
|
// Get the management plugin
|
|
var mgt = getNodeAttr(node, 'mgt');
|
|
|
|
// Create an instance of the plugin
|
|
var plugin;
|
|
switch (mgt) {
|
|
case "kvm":
|
|
plugin = new kvmPlugin();
|
|
break;
|
|
case "esx":
|
|
plugin = new esxPlugin();
|
|
break;
|
|
case "zvm":
|
|
plugin = new zvmPlugin();
|
|
break;
|
|
}
|
|
|
|
// Select tab
|
|
plugin.loadServiceInventory(data);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Select new tab
|
|
serviceTabs.select(tabId);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie for group names
|
|
*
|
|
* @param data Data from HTTP request
|
|
*/
|
|
function setGroupCookies(data) {
|
|
if (data.rsp) {
|
|
var groups = new Array();
|
|
|
|
// Index 0 is the table header
|
|
var cols, name, ip, hostname, desc, selectable, comments, tmp;
|
|
for (var i = 1; i < data.rsp.length; i++) {
|
|
// Set default description and selectable
|
|
selectable = "no";
|
|
desc = "No description";
|
|
|
|
// Split into columns:
|
|
// node, ip, hostnames, otherinterfaces, comments, disable
|
|
cols = data.rsp[i].split(',');
|
|
name = cols[0].replace(new RegExp('"', 'g'), '');
|
|
ip = cols[1].replace(new RegExp('"', 'g'), '');
|
|
hostname = cols[2].replace(new RegExp('"', 'g'), '');
|
|
|
|
// It should return: "description: All machines; network: 10.1.100.0/24;"
|
|
comments = cols[4].replace(new RegExp('"', 'g'), '');
|
|
tmp = comments.split('|');
|
|
for (var j = 0; j < tmp.length; j++) {
|
|
// Save description
|
|
if (tmp[j].indexOf('description:') > -1) {
|
|
desc = tmp[j].replace('description:', '');
|
|
desc = jQuery.trim(desc);
|
|
}
|
|
|
|
// Is the group selectable?
|
|
if (tmp[j].indexOf('selectable:') > -1) {
|
|
selectable = tmp[j].replace('selectable:', '');
|
|
selectable = jQuery.trim(selectable);
|
|
}
|
|
}
|
|
|
|
// Save groups that are selectable
|
|
if (selectable == "yes")
|
|
groups.push(name + ':' + ip + ':' + hostname + ':' + desc);
|
|
}
|
|
|
|
// Set cookie to expire in 60 minutes
|
|
var exDate = new Date();
|
|
exDate.setTime(exDate.getTime() + (240 * 60 * 1000));
|
|
$.cookie('srv_groups', groups, { expires: exDate });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
var provMethodPos = 0;
|
|
var comments = 0;
|
|
var desc, selectable, tmp;
|
|
// Get column index for each attribute
|
|
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;
|
|
|
|
case 'comments':{
|
|
comments = i;
|
|
}
|
|
break;
|
|
|
|
case 'provmethod':{
|
|
provMethodPos = 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 provMethod = cols[provMethodPos].replace(new RegExp('"', 'g'), '');
|
|
var osVer = cols[osversPos].replace(new RegExp('"', 'g'), '');
|
|
var osArch = cols[osarchPos].replace(new RegExp('"', 'g'), '');
|
|
var osComments = cols[comments].replace(new RegExp('"', 'g'), '');
|
|
|
|
// Only save install boot
|
|
if (provMethod.indexOf('install') > -1) {
|
|
if (osComments) {
|
|
// Only enable images where description and selectable comments exist
|
|
// Set default description and selectable
|
|
selectable = "no";
|
|
desc = "No description";
|
|
|
|
tmp = osComments.split('|');
|
|
for (var j = 0; j < tmp.length; j++) {
|
|
// Save description
|
|
if (tmp[j].indexOf('description:') > -1) {
|
|
desc = tmp[j].replace('description:', '');
|
|
desc = jQuery.trim(desc);
|
|
}
|
|
|
|
// Is the image selectable?
|
|
if (tmp[j].indexOf('selectable:') > -1) {
|
|
selectable = tmp[j].replace('selectable:', '');
|
|
selectable = jQuery.trim(selectable);
|
|
}
|
|
}
|
|
|
|
// Save images that are selectable
|
|
if (selectable == "yes")
|
|
imageNames.push(osImage + ':' + desc);
|
|
}
|
|
|
|
profilesHash[profile] = 1;
|
|
osVersHash[osVer] = 1;
|
|
osArchsHash[osArch] = 1;
|
|
}
|
|
}
|
|
|
|
// Save image names in a cookie
|
|
$.cookie('srv_imagenames', imageNames);
|
|
|
|
// Save profiles in a cookie
|
|
var tmp = new Array;
|
|
for (var key in profilesHash) {
|
|
tmp.push(key);
|
|
}
|
|
$.cookie('srv_profiles', tmp);
|
|
|
|
// Save OS versions in a cookie
|
|
tmp = new Array;
|
|
for (var key in osVersHash) {
|
|
tmp.push(key);
|
|
}
|
|
$.cookie('srv_osvers', tmp);
|
|
|
|
// Save OS architectures in a cookie
|
|
tmp = new Array;
|
|
for (var key in osArchsHash) {
|
|
tmp.push(key);
|
|
}
|
|
$.cookie('srv_osarchs', tmp);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Set a cookie for user nodes
|
|
*
|
|
* @param data Data from HTTP request
|
|
*/
|
|
function setUserNodes(data) {
|
|
if (data.rsp) {
|
|
// Get user name that is logged in
|
|
var userName = $.cookie('xcat_username');
|
|
var usrNodes = new Array();
|
|
|
|
// Ignore first columns because it is the header
|
|
for ( var i = 1; i < data.rsp.length; i++) {
|
|
// Go through each column
|
|
// where column names are: node, os, arch, profile, provmethod, supportedarchs, nodetype, comments, disable
|
|
var cols = data.rsp[i].split(',');
|
|
var node = cols[0].replace(new RegExp('"', 'g'), '');
|
|
|
|
// Comments can contain the owner and description
|
|
var comments = new Array();
|
|
if (cols[7].indexOf(';') > -1) {
|
|
comments = cols[7].replace(new RegExp('"', 'g'), '').split(';');
|
|
} else {
|
|
comments.push(cols[7].replace(new RegExp('"', 'g'), ''));
|
|
}
|
|
|
|
// Extract the owner
|
|
var owner;
|
|
for (var j in comments) {
|
|
if (comments[j].indexOf('owner:') > -1) {
|
|
owner = comments[j].replace('owner:', '');
|
|
|
|
if (owner == userName) {
|
|
usrNodes.push(node);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
} // End of for
|
|
|
|
// Set cookie to expire in 240 minutes
|
|
var exDate = new Date();
|
|
exDate.setTime(exDate.getTime() + (240 * 60 * 1000));
|
|
$.cookie(userName + '_usrnodes', usrNodes, { expires: exDate });
|
|
} // End of if
|
|
}
|
|
|
|
/**
|
|
* Power on a given node
|
|
*
|
|
* @param tgtNodes Node to power on or off
|
|
* @param power2 Power node to given state
|
|
*/
|
|
function powerNode(tgtNodes, power2) {
|
|
// Show power loader
|
|
var nodesDTId = 'userNodesDT';
|
|
var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
|
|
powerCol.find('img').show();
|
|
|
|
var nodes = tgtNodes.split(',');
|
|
for (var n in nodes) {
|
|
// Get hardware that was selected
|
|
var hw = getUserNodeAttr(nodes[n], 'mgt');
|
|
|
|
// Change to power softoff (to gracefully shutdown)
|
|
switch (hw) {
|
|
case "blade":
|
|
break;
|
|
case "hmc":
|
|
break;
|
|
case "ipmi":
|
|
break;
|
|
case "zvm":
|
|
if (power2 == 'off') {
|
|
power2 = 'softoff';
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'rpower',
|
|
tgt : tgtNodes,
|
|
args : power2,
|
|
msg : tgtNodes
|
|
},
|
|
|
|
success : updatePowerStatus
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update power status of a node in the datatable
|
|
*
|
|
* @param data Data from HTTP request
|
|
*/
|
|
function updatePowerStatus(data) {
|
|
// Get datatable
|
|
var nodesDTId = 'userNodesDT';
|
|
var dTable = $('#' + nodesDTId).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, '#' + nodesDTId, 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]);
|
|
}
|
|
}
|
|
|
|
var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)');
|
|
powerCol.find('img').hide();
|
|
adjustColumnSize(nodesDTId);
|
|
}
|
|
|
|
/**
|
|
* 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 nodesDTId = 'userNodesDT';
|
|
var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)');
|
|
gangliaCol.find('img').show();
|
|
|
|
if (monitor == 'on') {
|
|
if (node) {
|
|
// Check if ganglia RPMs are installed
|
|
$.ajax( {
|
|
url : 'lib/srv_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
|
|
* @return Nothing
|
|
*/
|
|
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/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : 'gangliastart;' + data.msg,
|
|
msg : data.msg
|
|
},
|
|
|
|
success : function(data) {
|
|
// Remove any warnings
|
|
$('#nodesTab').find('.ui-state-error').remove();
|
|
refreshGangliaStatus(data.msg);
|
|
}
|
|
});
|
|
} // End of if (warn)
|
|
} // End of function(data)
|
|
});
|
|
}
|
|
} else {
|
|
var args;
|
|
if (node) {
|
|
args = 'gangliastop;' + node;
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : args,
|
|
msg : node
|
|
},
|
|
|
|
success : function(data) {
|
|
refreshGangliaStatus(data.msg);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open a dialog to clone node
|
|
*
|
|
* @param tgtNodes Nodes to clone
|
|
*/
|
|
function cloneNode(tgtNodes) {
|
|
var userName = $.cookie('xcat_username');
|
|
var nodes = tgtNodes.split(',');
|
|
var tmp = $.cookie(userName + '_usrnodes');
|
|
var usrNodes = tmp.split(',');
|
|
|
|
var maxVM = parseInt($.cookie(userName + '_maxvm'));
|
|
|
|
// Do not allow user to clone if the maximum number of VMs is reached
|
|
if (usrNodes.length >= maxVM) {
|
|
var warn = createWarnBar('You have reached the maximum number of virtual machines allowed (' + maxVM + '). Delete un-used virtual machines or contact your system administrator request more virtual machines.');
|
|
warn.prependTo($('#manageTab'));
|
|
return;
|
|
}
|
|
|
|
for (var n in nodes) {
|
|
// Get hardware that was selected
|
|
var hw = getUserNodeAttr(nodes[n], 'mgt');
|
|
|
|
// Create an instance of the plugin
|
|
var plugin;
|
|
switch (hw) {
|
|
case "kvm":
|
|
plugin = new kvmPlugin();
|
|
break;
|
|
case "esx":
|
|
plugin = new esxPlugin();
|
|
break;
|
|
case "zvm":
|
|
plugin = new zvmPlugin();
|
|
break;
|
|
}
|
|
|
|
// Clone node
|
|
plugin.serviceClone(nodes[n]);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Open a dialog to delete node
|
|
*
|
|
* @param tgtNodes Nodes to delete
|
|
*/
|
|
function deleteNode(tgtNodes) {
|
|
var nodes = tgtNodes.split(',');
|
|
|
|
// Loop through each node and create target nodes string
|
|
var tgtNodesStr = '';
|
|
for (var i in nodes) {
|
|
if (i == 0 && i == nodes.length - 1) {
|
|
// If it is the 1st and only node
|
|
tgtNodesStr += nodes[i];
|
|
} else if (i == 0 && i != nodes.length - 1) {
|
|
// If it is the 1st node of many nodes, append a comma to the string
|
|
tgtNodesStr += nodes[i] + ', ';
|
|
} else {
|
|
if (i == nodes.length - 1) {
|
|
// If it is the last node, append nothing to the string
|
|
tgtNodesStr += nodes[i];
|
|
} else {
|
|
// Append a comma to the string
|
|
tgtNodesStr += nodes[i] + ', ';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Confirm delete of node
|
|
var dialog = $('<div></div>');
|
|
var warn = createWarnBar('Are you sure you want to delete ' + tgtNodesStr + '?');
|
|
dialog.append(warn);
|
|
|
|
// Open dialog
|
|
dialog.dialog({
|
|
title: "Confirm",
|
|
modal: true,
|
|
close: function(){
|
|
$(this).remove();
|
|
},
|
|
width: 400,
|
|
buttons: {
|
|
"Yes": function(){
|
|
// Create status bar and append to tab
|
|
var instance = 0;
|
|
var statBarId = 'deleteStat' + instance;
|
|
while ($('#' + statBarId).length) {
|
|
// If one already exists, generate another one
|
|
instance = instance + 1;
|
|
statBarId = 'deleteStat' + instance;
|
|
}
|
|
|
|
var statBar = createStatusBar(statBarId);
|
|
var loader = createLoader('');
|
|
statBar.find('div').append(loader);
|
|
statBar.prependTo($('#manageTab'));
|
|
|
|
// Delete the virtual server
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'rmvm',
|
|
tgt : tgtNodes,
|
|
args : '',
|
|
msg : 'out=' + statBarId + ';cmd=rmvm;tgt=' + tgtNodes
|
|
},
|
|
|
|
success : function(data) {
|
|
var args = data.msg.split(';');
|
|
var statBarId = args[0].replace('out=', '');
|
|
var tgts = args[2].replace('tgt=', '').split(',');
|
|
|
|
// Get data table
|
|
var nodesDTId = 'userNodesDT';
|
|
var dTable = $('#' + nodesDTId).dataTable();
|
|
var failed = false;
|
|
|
|
// Create an info box to show output
|
|
var output = writeRsp(data.rsp, '');
|
|
output.css('margin', '0px');
|
|
// Remove loader and append output
|
|
$('#' + statBarId + ' img').remove();
|
|
$('#' + statBarId + ' div').append(output);
|
|
|
|
// If there was an error, do not continue
|
|
if (output.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], '#' + nodesDTId, 1);
|
|
dTable.fnDeleteRow(rowPos);
|
|
}
|
|
}
|
|
|
|
// Refresh nodes owned by user
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'tabdump',
|
|
tgt : '',
|
|
args : 'nodetype',
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
setUserNodes(data);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$(this).dialog("close");
|
|
},
|
|
"No": function() {
|
|
$(this).dialog("close");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Unlock a node by setting the ssh keys
|
|
*
|
|
* @param tgtNodes Nodes to unlock
|
|
*/
|
|
function unlockNode(tgtNodes) {
|
|
var nodes = tgtNodes.split(',');
|
|
|
|
// Loop through each node and create target nodes string
|
|
var tgtNodesStr = '';
|
|
for (var i in nodes) {
|
|
if (i == 0 && i == nodes.length - 1) {
|
|
// If it is the 1st and only node
|
|
tgtNodesStr += nodes[i];
|
|
} else if (i == 0 && i != nodes.length - 1) {
|
|
// If it is the 1st node of many nodes, append a comma to the string
|
|
tgtNodesStr += nodes[i] + ', ';
|
|
} else {
|
|
if (i == nodes.length - 1) {
|
|
// If it is the last node, append nothing to the string
|
|
tgtNodesStr += nodes[i];
|
|
} else {
|
|
// Append a comma to the string
|
|
tgtNodesStr += nodes[i] + ', ';
|
|
}
|
|
}
|
|
}
|
|
|
|
var dialog = $('<div></div>');
|
|
var infoBar = createInfoBar('Give the root password for this node range to setup its SSH keys.');
|
|
dialog.append(infoBar);
|
|
|
|
var unlockForm = $('<div class="form"></div>').css('margin', '5px');
|
|
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>');
|
|
dialog.append(unlockForm);
|
|
|
|
dialog.find('div input').css('margin', '5px');
|
|
|
|
// 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"
|
|
}
|
|
});
|
|
|
|
// Open dialog
|
|
dialog.dialog({
|
|
title: "Confirm",
|
|
modal: true,
|
|
close: function(){
|
|
$(this).remove();
|
|
},
|
|
width: 450,
|
|
buttons: {
|
|
"Ok": function(){
|
|
// Create status bar and append to tab
|
|
var instance = 0;
|
|
var statBarId = 'unlockStat' + instance;
|
|
while ($('#' + statBarId).length) {
|
|
// If one already exists, generate another one
|
|
instance = instance + 1;
|
|
statBarId = 'unlockStat' + instance;
|
|
}
|
|
|
|
var statBar = createStatusBar(statBarId);
|
|
var loader = createLoader('');
|
|
statBar.find('div').append(loader);
|
|
statBar.prependTo($('#manageTab'));
|
|
|
|
// If a password is given
|
|
var password = unlockForm.find('input[name=password]:eq(0)');
|
|
if (password.val()) {
|
|
// Setup SSH keys
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : 'unlock;' + tgtNodes + ';' + password.val(),
|
|
msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes
|
|
},
|
|
|
|
success : function(data) {
|
|
// Create an info box to show output
|
|
var output = writeRsp(data.rsp, '');
|
|
output.css('margin', '0px');
|
|
// Remove loader and append output
|
|
$('#' + statBarId + ' img').remove();
|
|
$('#' + statBarId + ' div').append(output);
|
|
}
|
|
});
|
|
|
|
$(this).dialog("close");
|
|
}
|
|
},
|
|
"Cancel": function() {
|
|
$(this).dialog("close");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get nodes current load information
|
|
*/
|
|
function getNodesCurrentLoad(){
|
|
var userName = $.cookie('xcat_username');
|
|
var nodes = $.cookie(userName + '_usrnodes');
|
|
|
|
// Get nodes current status
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : 'gangliacurrent;node;' + nodes,
|
|
msg : ''
|
|
},
|
|
|
|
success: saveNodeLoad
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Save node load data
|
|
*
|
|
* @param status Data returned from HTTP request
|
|
*/
|
|
function saveNodeLoad(status){
|
|
// Save node path and status for future use
|
|
nodePath = new Object();
|
|
nodeStatus = new Object();
|
|
|
|
// Get nodes status
|
|
var nodes = status.rsp[0].split(';');
|
|
|
|
var i = 0, pos = 0;
|
|
var node = '', tmpStr = '';
|
|
var tmpArry;
|
|
|
|
for (i = 0; i < nodes.length; i++){
|
|
tmpStr = nodes[i];
|
|
pos = tmpStr.indexOf(':');
|
|
node = tmpStr.substring(0, pos);
|
|
tmpArry = tmpStr.substring(pos + 1).split(',');
|
|
|
|
switch(tmpArry[0]){
|
|
case 'UNKNOWN':{
|
|
nodeStatus[node] = -2;
|
|
}
|
|
break;
|
|
case 'ERROR':{
|
|
nodeStatus[node] = -1;
|
|
}
|
|
break;
|
|
case 'WARNING':{
|
|
nodeStatus[node] = 0;
|
|
nodePath[node] = tmpArry[1];
|
|
}
|
|
break;
|
|
case 'NORMAL':{
|
|
nodeStatus[node] = 1;
|
|
nodePath[node] = tmpArry[1];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get monitoring metrics and load into inventory fieldset
|
|
*
|
|
* @param node Node to collect metrics
|
|
*/
|
|
function getMonitorMetrics(node) {
|
|
// Inventory tab should have this fieldset already created
|
|
// e.g. <fieldset id="gpok123_monitor"></fieldset>
|
|
$('#' + node + '_monitor').children('div').remove();
|
|
|
|
// Before trying to get the metrics, check if Ganglia is running
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : 'gangliastatus;' + node,
|
|
msg : ''
|
|
},
|
|
|
|
success: function(data) {
|
|
var ganglia = data.rsp;
|
|
var node, status;
|
|
|
|
// Get the ganglia 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 && status == 'on') {
|
|
// Get monitoring metrics
|
|
$.ajax({
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webrun',
|
|
tgt : '',
|
|
args : 'gangliashow;' + nodePath[node] + ';hour;_summary_',
|
|
msg : node
|
|
},
|
|
|
|
success: drawMonitoringCharts
|
|
});
|
|
} else if (node && status == 'off') {
|
|
var info = createInfoBar('Ganglia monitoring is disabled for this node');
|
|
$('#' + node + '_monitor').append(info.css('width', '300px'));
|
|
}
|
|
} // End of for
|
|
} // End of function
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Draw monitoring charts based on node metrics
|
|
*
|
|
* @param data Data returned from HTTP request
|
|
*/
|
|
function drawMonitoringCharts(data){
|
|
var nodeMetrics = new Object();
|
|
var metricData = data.rsp[0].split(';');
|
|
var node = data.msg;
|
|
|
|
var metricName = '';
|
|
var metricVal = '';
|
|
var pos = 0;
|
|
|
|
// Go through the metrics returned
|
|
for (var m = 0; m < metricData.length; m++){
|
|
pos = metricData[m].indexOf(':');
|
|
|
|
// Get metric name
|
|
metricName = metricData[m].substr(0, pos);
|
|
nodeMetrics[metricName] = new Array();
|
|
// Get metric values
|
|
metricVal = metricData[m].substr(pos + 1).split(',');
|
|
// Save node metrics
|
|
for (var i = 0; i < metricVal.length; i++){
|
|
nodeMetrics[metricName].push(Number(metricVal[i]));
|
|
}
|
|
}
|
|
|
|
drawLoadFlot(node, nodeMetrics['load_one'], nodeMetrics['cpu_num']);
|
|
drawCpuFlot(node, nodeMetrics['cpu_idle']);
|
|
drawMemFlot(node, nodeMetrics['mem_free'], nodeMetrics['mem_total']);
|
|
drawDiskFlot(node, nodeMetrics['disk_free'], nodeMetrics['disk_total']);
|
|
drawNetworkFlot(node, nodeMetrics['bytes_in'], nodeMetrics['bytes_out']);
|
|
}
|
|
|
|
/**
|
|
* Draw load metrics flot
|
|
*
|
|
* @param node Node name
|
|
* @param loadpair Load timestamp and value pair
|
|
* @param cpupair CPU number and value pair
|
|
*/
|
|
function drawLoadFlot(node, loadPair, cpuPair){
|
|
var load = new Array();
|
|
var cpu = new Array();
|
|
|
|
var i = 0;
|
|
var yAxisMax = 0;
|
|
var interval = 1;
|
|
|
|
// Append flot to node monitoring fieldset
|
|
var loadFlot = $('<div id="' + node + '_load"></div>').css({
|
|
'float': 'left',
|
|
'height': '150px',
|
|
'margin': '0 0 10px',
|
|
'width': '300px'
|
|
});
|
|
$('#' + node + '_monitor').append(loadFlot);
|
|
$('#' + node + '_load').empty();
|
|
|
|
// Parse load pair where:
|
|
// timestamp must be mutiplied by 1000 and Javascript timestamp is in ms
|
|
for (i = 0; i < loadPair.length; i += 2){
|
|
load.push([loadPair[i] * 1000, loadPair[i + 1]]);
|
|
if (loadPair[i + 1] > yAxisMax){
|
|
yAxisMax = loadPair[i + 1];
|
|
}
|
|
}
|
|
|
|
// Parse CPU pair
|
|
for (i = 0; i < cpuPair.length; i += 2){
|
|
cpu.push([cpuPair[i] * 1000, cpuPair[i + 1]]);
|
|
if (cpuPair[i + 1] > yAxisMax){
|
|
yAxisMax = cpuPair[i + 1];
|
|
}
|
|
}
|
|
|
|
interval = parseInt(yAxisMax / 3);
|
|
if (interval < 1){
|
|
interval = 1;
|
|
}
|
|
|
|
$.jqplot(node + '_load', [load, cpu],{
|
|
title: ' 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 CPU usage flot
|
|
*
|
|
* @param node Node name
|
|
* @param cpuPair CPU timestamp and value pair
|
|
*/
|
|
function drawCpuFlot(node, cpuPair){
|
|
var cpu = new Array();
|
|
|
|
// Append flot to node monitoring fieldset
|
|
var cpuFlot = $('<div id="' + node + '_cpu"></div>').css({
|
|
'float': 'left',
|
|
'height': '150px',
|
|
'margin': '0 0 10px',
|
|
'width': '300px'
|
|
});
|
|
$('#' + node + '_monitor').append(cpuFlot);
|
|
$('#' + node + '_cpu').empty();
|
|
|
|
// Time stamp should by mutiplied by 1000
|
|
// CPU idle comes from server, subtract 1 from idle
|
|
for(var i = 0; i < cpuPair.length; i +=2){
|
|
cpu.push([(cpuPair[i] * 1000), (100 - cpuPair[i + 1])]);
|
|
}
|
|
|
|
$.jqplot(node + '_cpu', [cpu],{
|
|
title: '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 memory usage flot
|
|
*
|
|
* @param node Node name
|
|
* @param freePair Free memory timestamp and value pair
|
|
* @param totalPair Total memory timestamp and value pair
|
|
*/
|
|
function drawMemFlot(node, freePair, totalPair){
|
|
var used = new Array();
|
|
var total = new Array();
|
|
var size = 0;
|
|
|
|
// Append flot to node monitoring fieldset
|
|
var memoryFlot = $('<div id="' + node + '_memory"></div>').css({
|
|
'float': 'left',
|
|
'height': '150px',
|
|
'margin': '0 0 10px',
|
|
'width': '300px'
|
|
});
|
|
$('#' + node + '_monitor').append(memoryFlot);
|
|
$('#' + node + '_memory').empty();
|
|
|
|
if(freePair.length < totalPair.length){
|
|
size = freePair.length;
|
|
} else {
|
|
size = freePair.length;
|
|
}
|
|
|
|
var tmpTotal, tmpUsed;
|
|
for(var i = 0; i < size; i+=2){
|
|
tmpTotal = totalPair[i+1];
|
|
tmpUsed = tmpTotal-freePair[i+1];
|
|
tmpTotal = tmpTotal/1000000;
|
|
tmpUsed = tmpUsed/1000000;
|
|
total.push([totalPair[i]*1000, tmpTotal]);
|
|
used.push([freePair[i]*1000, tmpUsed]);
|
|
}
|
|
|
|
$.jqplot(node + '_memory', [used, total],{
|
|
title: '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}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Draw disk usage flot
|
|
*
|
|
* @param node Node name
|
|
* @param freePair Free disk space (Ganglia only logs free data)
|
|
* @param totalPair Total disk space
|
|
*/
|
|
function drawDiskFlot(node, freePair, totalPair) {
|
|
var used = new Array();
|
|
var total = new Array();
|
|
var size = 0;
|
|
|
|
// Append flot to node monitoring fieldset
|
|
var diskFlot = $('<div id="' + node + '_disk"></div>').css({
|
|
'float' : 'left',
|
|
'height' : '150px',
|
|
'margin' : '0 0 10px',
|
|
'width' : '300px'
|
|
});
|
|
$('#' + node + '_monitor').append(diskFlot);
|
|
$('#' + node + '_disk').empty();
|
|
|
|
if (freePair.length < totalPair.length) {
|
|
size = freePair.length;
|
|
} else {
|
|
size = freePair.length;
|
|
}
|
|
|
|
var tmpTotal, tmpUsed;
|
|
for ( var i = 0; i < size; i += 2) {
|
|
tmpTotal = totalPair[i + 1];
|
|
tmpUsed = tmpTotal - freePair[i + 1];
|
|
total.push([ totalPair[i] * 1000, tmpTotal ]);
|
|
used.push([ freePair[i] * 1000, tmpUsed ]);
|
|
}
|
|
|
|
$.jqplot(node + '_disk', [ used, total ], {
|
|
title : 'Disk 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
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Draw network usage flot
|
|
*
|
|
* @param node Node name
|
|
* @param freePair Free memory timestamp and value pair
|
|
* @param totalPair Total memory timestamp and value pair
|
|
*/
|
|
function drawNetworkFlot(node, inPair, outPair) {
|
|
var inArray = new Array();
|
|
var outArray = new Array();
|
|
var maxVal = 0;
|
|
var unitName = 'B';
|
|
var divisor = 1;
|
|
|
|
// Append flot to node monitoring fieldset
|
|
var diskFlot = $('<div id="' + node + '_network"></div>').css({
|
|
'float' : 'left',
|
|
'height' : '150px',
|
|
'margin' : '0 0 10px',
|
|
'width' : '300px'
|
|
});
|
|
$('#' + node + '_monitor').append(diskFlot);
|
|
$('#' + node + '_network').empty();
|
|
|
|
for (var i = 0; i < inPair.length; i += 2) {
|
|
if (inPair[i + 1] > maxVal) {
|
|
maxVal = inPair[i + 1];
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < outPair.length; i += 2) {
|
|
if (outPair[i + 1] > maxVal) {
|
|
maxVal = outPair[i + 1];
|
|
}
|
|
}
|
|
|
|
if (maxVal > 3000000) {
|
|
divisor = 1000000;
|
|
unitName = 'GB';
|
|
} else if (maxVal >= 3000) {
|
|
divisor = 1000;
|
|
unitName = 'MB';
|
|
} else {
|
|
// Do nothing
|
|
}
|
|
|
|
for (i = 0; i < inPair.length; i += 2) {
|
|
inArray.push([ (inPair[i] * 1000), (inPair[i + 1] / divisor) ]);
|
|
}
|
|
|
|
for (i = 0; i < outPair.length; i += 2) {
|
|
outArray.push([ (outPair[i] * 1000), (outPair[i + 1] / divisor) ]);
|
|
}
|
|
|
|
$.jqplot(node + '_network', [ inArray, outArray ], {
|
|
title : 'Network Last Hour',
|
|
axes : {
|
|
xaxis : {
|
|
renderer : $.jqplot.DateAxisRenderer,
|
|
numberTicks : 4,
|
|
tickOptions : {
|
|
formatString : '%R',
|
|
show : true
|
|
}
|
|
},
|
|
yaxis : {
|
|
min : 0,
|
|
tickOptions : {
|
|
formatString : '%d' + unitName
|
|
}
|
|
}
|
|
},
|
|
legend : {
|
|
show : true,
|
|
location : 'nw'
|
|
},
|
|
series : [ {
|
|
label : 'In'
|
|
}, {
|
|
label : 'Out'
|
|
} ],
|
|
seriesDefaults : {
|
|
showMarker : false
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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 = null;
|
|
|
|
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 the maximum number of VMs a user could have
|
|
*/
|
|
function setMaxVM() {
|
|
var userName = $.cookie('xcat_username');
|
|
|
|
$.ajax( {
|
|
url : 'lib/srv_cmd.php',
|
|
dataType : 'json',
|
|
data : {
|
|
cmd : 'webportal',
|
|
tgt : '',
|
|
args : 'getmaxvm;' + userName,
|
|
msg : ''
|
|
},
|
|
|
|
success : function(data) {
|
|
// Get response
|
|
var rsp = jQuery.trim(data.rsp);
|
|
rsp = rsp.replace('Max allowed:', '');
|
|
|
|
// Set cookie to expire in 60 minutes
|
|
var exDate = new Date();
|
|
exDate.setTime(exDate.getTime() + (240 * 60 * 1000));
|
|
$.cookie(userName + '_maxvm', rsp, { expires: exDate });
|
|
}
|
|
});
|
|
} |