blob: 12b7f8666659c093a6cd349bd6ac7642b6c00dbc [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/* Declarations of global data. */
var fetchClusterServicesPoller;
var clusterServices;
// Storing globally for the sake of multiple screens in reconfigure
var localReconfigureServiceData = {};
var remoteReconfigureServiceData = {};
var confirmationDataPanelBodyContent = '';
var confirmationDataPanel;
var panelNoButton = {
value: 'Cancel',
action: function (e) {
e.preventDefault();
hideAndDestroyPanel();
},
section: 'footer'
};
var panelYesButton;
// Only one service can be reconfigured at a time.
var reconfigLevelOneYesButton;
var reconfigLevelTwoNoButton;
function showPanel() {
showPanel(function() {});
}
function showPanel(postShowFn) {
confirmationDataPanel.set('y', 200);
confirmationDataPanel.set('x', (globalYui.one('body').get('region').width - confirmationDataPanel.get('width'))/2);
confirmationDataPanel.show();
if (postShowFn != null) {
postShowFn.call();
}
}
function hidePanel(postHideFn) {
if (postHideFn != null) {
postHideFn.call();
}
}
function hideAndDestroyPanel() {
hidePanel(function() {
confirmationDataPanel.hide();
destroyInformationalPanel(confirmationDataPanel);
});
}
function getTitleForReconfiguration(serviceName) {
return 'Make Configuration Changes for ' + serviceName;
}
function setupReconfigureFirstScreen(serviceName) {
var panelTitle = getTitleForReconfiguration(serviceName);
confirmationDataPanel.set( 'headerContent', panelTitle);
confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent);
// Remove buttons from previous stage
confirmationDataPanel.removeButton(0);
confirmationDataPanel.removeButton(0);
confirmationDataPanel.addButton( panelNoButton );
confirmationDataPanel.addButton( reconfigLevelOneYesButton );
}
function setupReconfigureSecondScreen(serviceName) {
var affectedServices = clusterServices[serviceName].dependencies;
var dependents = clusterServices[serviceName].dependents;
for (dep in dependents) {
affectedServices.push(dependents[dep]);
}
var panelContent = 'Affected services:' + getAffectedDependenciesMarkup(affectedServices, serviceName, 'reconfigure');
var panelTitle = 'Review changes to ' + serviceName + '\'s configuration';
confirmationDataPanel.set( 'headerContent', panelTitle);
confirmationDataPanel.set( 'bodyContent', panelContent);
// Remove buttons from previous stage
confirmationDataPanel.removeButton(0);
confirmationDataPanel.removeButton(0);
confirmationDataPanel.addButton( reconfigLevelTwoNoButton );
confirmationDataPanel.addButton( panelYesButton );
}
// Clean up the affected-services list to only include appropriate installed long-running services
function getAffectedDependenciesMarkup(affectedServices, serviceName, action) {
var affectedDependenciesMarkup = '';
var serviceDisplayName = clusterServices[serviceName].displayName;
var deps = affectedServices;
affectedServices = [];
for (dep in deps) {
var svc = deps[dep];
if (clusterServices.hasOwnProperty(svc) && (clusterServices[svc].isEnabled == 1) && clusterServices[svc].attributes.runnable ) {
affectedServices.push(svc);
}
}
var dependencyMarkup = "";
for (affectedSrvc in affectedServices) {
if (clusterServices[affectedServices[affectedSrvc]].attributes.runnable) {
dependencyMarkup += '<tr><td>' + clusterServices[affectedServices[affectedSrvc]].displayName + '</td><td>' + titleCase(clusterServices[affectedServices[affectedSrvc]].state) + '</td></tr>';
}
}
if (dependencyMarkup != '') {
// Add this service at the top of the list
dependencyMarkup = '<table><thead><th>Service name</th><th>Current state</th></thead><tr><td>' + serviceDisplayName + '</td><td>' + titleCase(clusterServices[serviceName].state) + '</td></tr>' + dependencyMarkup + '</table>';
affectedDependenciesMarkup += 'Including this service and all its recursive dependencies, the following is the list of services that will be affected by ' + action + ' of ' + serviceName + ' :' +
'<br/>' +
'<div id="manageServicesDisplayDepsOnAction">' +
dependencyMarkup +
'</div>';
}
return affectedDependenciesMarkup;
}
function setupStartServiceScreen(serviceName) {
setupStartStopServiceScreen('start', serviceName);
}
function setupStopServiceScreen(serviceName) {
setupStartStopServiceScreen('stop', serviceName);
}
function setupStartStopServiceScreen(action, serviceName) {
var serviceDisplayName = clusterServices[serviceName].displayName;
var affectedServices;
var confirmationDataPanelTitle;
if ( action == 'start') {
confirmationDataPanelTitle = 'Starting ' + serviceDisplayName;
confirmationDataPanelBodyContent = "We are now going to start " + serviceDisplayName + "...<br/><br/>";
affectedServices = clusterServices[serviceName].dependencies;
} else if (action == 'stop') {
confirmationDataPanelTitle = 'Stopping ' + serviceDisplayName;
confirmationDataPanelBodyContent = "We are now going to stop " + serviceDisplayName + "...<br/><br/>";
affectedServices = clusterServices[serviceName].dependents;
}
confirmationDataPanelBodyContent += getAffectedDependenciesMarkup(affectedServices, serviceName, action);
confirmationDataPanelBodyContent = '<div id="confirmationDataPanelBodyContent">' + confirmationDataPanelBodyContent + '</div>';
confirmationDataPanel.set( 'headerContent', confirmationDataPanelTitle);
confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent);
confirmationDataPanel.set( 'height', 400);
confirmationDataPanel.set( 'width', 800);
confirmationDataPanel.addButton( panelNoButton);
confirmationDataPanel.addButton( panelYesButton );
showPanel();
}
function setupStartAllServicesScreen() {
setupStartStopAllServicesScreen('startAll');
}
function setupStopAllServicesScreen() {
setupStartStopAllServicesScreen('stopAll');
}
function setupStartStopAllServicesScreen(action) {
var confirmationDataPanelTitle;
var confirmationDataPanelBodyContent;
if ( action == 'startAll' ) {
confirmationDataPanelTitle = 'Start All Services';
confirmationDataPanelBodyContent = "We are now going to start all services in the cluster";
} else if ( action == 'stopAll' ) {
confirmationDataPanelTitle = 'Stop All Services';
confirmationDataPanelBodyContent = "We are now going to stop all the services in the cluster";
}
confirmationDataPanel.set( 'headerContent', confirmationDataPanelTitle);
confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent);
confirmationDataPanel.set( 'height', 400);
confirmationDataPanel.set( 'width', 800);
confirmationDataPanel.addButton( panelNoButton);
confirmationDataPanel.addButton( panelYesButton );
showPanel();
}
function setupReconfigureScreens(serviceName) {
// TODO: Needed for others too?
/* First, (temporarily) stop any further fetches. */
fetchClusterServicesPoller.stop();
reconfigLevelOneYesButton = {
value: 'Apply Changes',
action: function (e) {
e.preventDefault();
localReconfigureServiceData = configureServicesUtil.generateUserOpts();
var remoteProps = remoteReconfigureServiceData.services[serviceName].properties;
var localProps = localReconfigureServiceData[serviceName].properties;
var allEqual = true;
for (key in localProps) {
var remoteValue = remoteProps[key].value;
var localValue = localProps[key]["value"];
if ( localValue != remoteValue) {
allEqual = false;
}
}
if (allEqual) {
alert("You haven't made any changes");
return;
}
hidePanel(function() {
// Store the requestData and the html
confirmationDataPanelBodyContent = confirmationDataPanel.get( 'bodyContent' );
setupReconfigureSecondScreen(serviceName);
showPanel();
});
},
classNames: 'okButton',
section: 'footer'
};
reconfigLevelTwoNoButton = {
value: 'Go back and re-edit',
action: function (e) {
e.preventDefault();
hidePanel(function() {
setupReconfigureFirstScreen(serviceName);
showPanel();
});
},
section: 'footer'
};
// Render first with a loading image and then get config items
confirmationDataPanelBodyContent =
"<img id=errorInfoPanelLoadingImgId class=loadingImg src=../images/loading.gif />";
var confirmationDataPanelTitle = getTitleForReconfiguration(serviceName);
confirmationDataPanel.set( 'height', 500);
confirmationDataPanel.set( 'width', 1000);
confirmationDataPanel.set( 'headerContent', confirmationDataPanelTitle);
confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent );
showPanel();
executeStage( '../php/frontend/fetchClusterServices.php?clusterName=' + clusterName +
'&getConfigs=true&serviceName=' + serviceName, function (serviceConfigurationData) {
// Store the remote data
remoteReconfigureServiceData = serviceConfigurationData;
var serviceConfigurationMarkup = configureServicesUtil.getOptionsSummaryMarkup(serviceConfigurationData, true);
if( globalYui.Lang.trim( serviceConfigurationMarkup).length == 0 ) {
serviceConfigurationMarkup = '<p>There is nothing to reconfigure for this service.</p>';
}
else {
/* Augment confirmationDataPanel with the relevant buttons only if there
* is something of value to show.
*/
confirmationDataPanel.addButton( panelNoButton );
confirmationDataPanel.addButton( reconfigLevelOneYesButton );
}
/* XXX Note that this must be kept in-sync with the corresponding markup
* on the InstallationWizard page.
*/
confirmationDataPanelBodyContent =
'<div id=formStatusDivId class=formStatusBar style="display:none">'+
'Placeholder' +
'</div>' +
'<div id=configureClusterAdvancedCoreDivId>' +
'<form id=configureClusterAdvancedFormId>' +
'<fieldset id=configureClusterAdvancedFieldSetId>' +
'<div id=configureClusterAdvancedDynamicRenderDivId>' +
serviceConfigurationMarkup +
'</div>' +
'</fieldset>' +
'</form>' +
'</div>';
confirmationDataPanelBodyContent = '<div id="confirmationDataPanelBodyContent">' + confirmationDataPanelBodyContent + '</div>';
confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent );
});
}
function performServiceManagement( action, serviceName, confirmationDataPanel ) {
/* First, (temporarily) stop any further fetches. */
fetchClusterServicesPoller.stop();
var manageServicesRequestData = {
action: action,
services: {}
};
if( action == "reconfigure" ) {
manageServicesRequestData.services = localReconfigureServiceData;
}
else {
/* Need to explicitly set a key named for serviceName this way because it's
* a variable - in the future, the value will be a filled-out array (for
* now, we only support managing a single service at a time).
*/
manageServicesRequestData.services[serviceName] = {};
}
globalYui.io( "../php/frontend/manageServices.php?clusterName=" + clusterName, {
method: 'POST',
data: globalYui.JSON.stringify(manageServicesRequestData),
timeout: 10000,
on: {
success: function(x, o) {
globalYui.log("RAW JSON DATA: " + o.responseText);
var manageServicesResponseJson;
try {
manageServicesResponseJson = globalYui.JSON.parse(o.responseText);
}
catch (e) {
alert("JSON Parse failed!");
return;
}
globalYui.log(globalYui.Lang.dump(manageServicesResponseJson));
/* Check that manageServicesResponseJson actually indicates success. */
if( manageServicesResponseJson.result == 0 ) {
/* Only on success should we destroy confirmationDataPanel - on
* failure, we depend on the fact that there'll be errors shown
* inside the panel that the user will want/need to interact with.
*/
hideAndDestroyPanel();
renderManageServicesProgress( manageServicesResponseJson.response );
}
else {
/* No need to hide confirmationDataPanel here - there are errors
* that need to be handled.
*/
if (action == 'reconfigure') {
hidePanel(function() {
setupReconfigureFirstScreen(serviceName);
showPanel( function() {
configureServicesUtil.handleConfigureServiceErrors( manageServicesResponseJson );
});
});
} else {
// Can't do anything for others
alert('Got error during ' + action + ' : ' + globalYui.Lang.dump(manageServicesResponseJson));
}
}
},
failure: function(x, o) {
alert("Async call failed!");
}
}
});
}
function getServiceConfigurationMarkup( serviceConfigurationData ) {
return serviceConfigurationMarkup;
}
function serviceManagementActionClickHandler( action, serviceName ) {
// Reinit the global content
confirmationDataPanelBodyContent = '';
var confirmationDataPanelTitle = ''; // Set title later
/* Create the panel that'll display our confirmation/data dialog. */
confirmationDataPanel =
createInformationalPanel( '#informationalPanelContainerDivId', confirmationDataPanelTitle );
panelYesButton = {
value: 'OK',
action: function (e) {
e.preventDefault();
performServiceManagement( action, serviceName, confirmationDataPanel );
},
classNames: 'okButton',
section: 'footer'
};
if ( action == 'start') {
setupStartServiceScreen(serviceName);
} else if ( action == 'stop') {
setupStopServiceScreen(serviceName);
} else if( action == 'startAll' ) {
setupStartAllServicesScreen();
} else if( action == 'stopAll' ) {
setupStopAllServicesScreen();
} else if( action == 'reconfigure' ) {
setupReconfigureScreens(serviceName);
}
}
function deduceServiceManagementEntryCssClass( serviceInfo ) {
var serviceManagementEntryCssClass = '';
var serviceState = serviceInfo.state;
if( serviceState.match(/^stop/i) || serviceState.match(/^fail/i) ) {
serviceManagementEntryCssClass = "serviceManagementEntryStopped";
}
else if( serviceState.match(/^start/i) ) {
serviceManagementEntryCssClass = "serviceManagementEntryStarted";
}
else if( serviceState.match(/^install/i) ) {
serviceManagementEntryCssClass = "serviceManagementEntryInstalled";
}
else if( serviceState.match(/^uninstall/i) ) {
serviceManagementEntryCssClass = "serviceManagementEntryUninstalled";
}
// globalYui.log( "Picking CSS class for" + serviceInfo.serviceName + ": " + serviceManagementEntryCssClass );
return serviceManagementEntryCssClass;
}
function generateServiceManagementEntryMarkup( serviceName, serviceInfo ) {
var generatedServiceManagementEntryMarkup = '';
var serviceAttributes = serviceInfo.attributes;
/* Only generate a Service Management entry for services that are:
*
* a) enabled
* b) runnable
* c) meant to be displayed
*/
if( (serviceInfo.isEnabled == true) && !serviceAttributes.noDisplay ) {
var serviceManagementEntryCssClass = deduceServiceManagementEntryCssClass( serviceInfo );
generatedServiceManagementEntryMarkup +=
'<li class="serviceManagementEntry '+ serviceManagementEntryCssClass + '">' +
'<div id="serviceManagementFor' + serviceName + '">' +
'<span class="serviceManagementEntryNameContainer">' +
'<a href="javascript:void(null)" name="' + serviceName + '" class="serviceManagementEntryName">' +
serviceInfo.displayName +
'</a>' +
'</span>' +
'<div class="serviceManagementEntryStateContainer">' +
titleCase(serviceInfo.state) +
'</div>' +
'<div class="serviceManagementEntryActionsContainer">';
if( serviceAttributes.runnable ) {
var serviceManagementEntryAnchorName = '';
var serviceManagementEntryAnchorTitle = '';
var serviceManagementEntryAnchorCssClasses = 'serviceManagementEntryAction btn ';
var serviceManagementEntryIconCssClass = '';
/* Already-started/stopped services shouldn't allow a start/stop operation on them. */
if( serviceInfo.state == 'STOPPED' || serviceInfo.state == 'FAILED') {
serviceManagementEntryAnchorName = 'start';
serviceManagementEntryAnchorTitle = 'Start';
serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStart';
serviceManagementEntryIconCssClass = 'iconic-play';
}
else if ( serviceInfo.state == 'STARTED' ) {
serviceManagementEntryAnchorName = 'stop';
serviceManagementEntryAnchorTitle = 'Stop';
serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStop';
serviceManagementEntryIconCssClass = 'iconic-stop';
}
else if ( serviceInfo.state == 'STOPPING') {
serviceManagementEntryAnchorName = 'start';
serviceManagementEntryAnchorTitle = 'Start';
serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStart disabled';
serviceManagementEntryIconCssClass = 'iconic-start disabled';
}
else if ( serviceInfo.state == 'STARTING') {
serviceManagementEntryAnchorName = 'stop';
serviceManagementEntryAnchorTitle = 'Stop';
serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStop disabled';
serviceManagementEntryIconCssClass = 'iconic-stop disabled';
}
generatedServiceManagementEntryMarkup +=
'<a href="javascript:void(null)" name="' + serviceManagementEntryAnchorName + '" ' +
'title="' + serviceManagementEntryAnchorTitle + '" ' +
'class="' + serviceManagementEntryAnchorCssClasses + '"><i class="' + serviceManagementEntryIconCssClass + '"></i></a> ';
}
var notReconfigurable = [ 'PIG', 'SQOOP', 'OOZIE', 'TEMPLETON', 'GANGLIA', 'HIVE' ];
var notReconfigurable = [ 'PIG', 'PIG2', 'SQOOP', 'OOZIE', 'TEMPLETON', 'GANGLIA', 'HIVE', 'HIVE2' ];
var reconfigureClass;
if (globalYui.Array.indexOf(notReconfigurable, serviceName) >= 0) {
reconfigureClass = 'serviceManagementEntryActionReconfigure disabled';
} else {
reconfigureClass = 'serviceManagementEntryActionReconfigure';
}
generatedServiceManagementEntryMarkup += '<a href="javascript:void(null)" name="reconfigure" title="Reconfigure" ' +
'class="btn serviceManagementEntryAction ' + reconfigureClass + '"><i class="iconic-cog"></i></a>' +
'</div>' +
'</div>' +
'</li>';
}
return generatedServiceManagementEntryMarkup;
}
// Do Not Remove --> We'll uncomment this section when the Service names link to something meaningful.
//
// /* Register click handlers for the service links themselves. */
// globalYui.one('#serviceManagementListId').delegate('click', function (e) {
// alert(this.getAttribute('name'));
// }, 'li.serviceManagementEntry span.serviceManagementEntryNameContainer a.serviceManagementEntryName' );
/* Register click handlers for the global-action buttons. */
globalYui.one('#serviceManagementGlobalActionsDivId').delegate('click', function (e) {
var action = this.getAttribute('name');
serviceManagementActionClickHandler( action );
}, 'button' );
/* Register click handlers for the action links for each service. */
globalYui.one('#serviceManagementListId').delegate('click', function (e) {
var action = this.getAttribute('name');
var serviceName = this.ancestor('li.serviceManagementEntry').
one('span.serviceManagementEntryNameContainer a.serviceManagementEntryName').getAttribute('name');
serviceManagementActionClickHandler( action, serviceName );
}, 'li.serviceManagementEntry div.serviceManagementEntryActionsContainer a.serviceManagementEntryAction' );
/* Main() */
/* The clusterName variable is set in the Javascript scaffolding spit out by manageServices.php */
var fetchClusterServicesPollerContext = {
source: '../php/frontend/fetchClusterServices.php',
schema: {
metaFields: {
services: 'response.services'
}
},
request: '?clusterName=' + clusterName,
/* TODO XXX Change this from 5 seconds to 1 minute. */
pollInterval: 5000,
maxFailedAttempts: 5
};
var fetchClusterServicesPollerResponseHandler = {
success: function (e, pdp) {
/* Clear the screen of the loading image (in case it's currently showing). */
hideLoadingImg();
/* The data from our backend. */
clusterServices = e.response.meta.services;
/* What we're here to render. */
var serviceManagementMarkup = '';
// Separate block for client-only software
/*
var clientOnlySoftwareMarkup = '';
for (var serviceName in clusterServices) {
var serviceInfo = clusterServices[serviceName];
if (clusterServices.hasOwnProperty(serviceName) && !serviceInfo.attributes.runnable) {
clientOnlySoftwareMarkup += generateServiceManagementEntryMarkup( serviceName, serviceInfo );
}
}
if (clientOnlySoftwareMarkup != '') {
serviceManagementMarkup += '<div class="serviceManagementGroup"><h2>Client-only software</h2><ul>';
serviceManagementMarkup += clientOnlySoftwareMarkup;
serviceManagementMarkup += '</div>';
}
*/
// Real services with server side components
serviceManagementMarkup += '<div class="serviceManagementGroup" style="margin-top:30px"><ul>';
for (var serviceName in clusterServices) {
var serviceInfo = clusterServices[serviceName];
if (clusterServices.hasOwnProperty(serviceName) && serviceInfo.attributes.runnable) {
serviceManagementMarkup += generateServiceManagementEntryMarkup( serviceName, serviceInfo );
}
}
serviceManagementMarkup += '</ul></div>';
/* Link the newly-generated serviceManagementMarkup into the DOM. */
globalYui.one("#serviceManagementDynamicRenderDivId").setContent( serviceManagementMarkup );
/* If serviceManagementMarkup is non-empty, unveil the contents of
* #serviceManagementGlobalActionsDivId (which contains the StartAll
* and StopAll buttons) as well.
*/
if( globalYui.Lang.trim( serviceManagementMarkup ).length > 0 ) {
globalYui.one("#serviceManagementGlobalActionsDivId").setStyle( 'display', 'block' );
}
},
failure: function (e, pdp) {
/* Clear the screen of the loading image (in case it's currently showing). */
hideLoadingImg();
alert('Failed to fetch cluster services!');
}
};
fetchClusterServicesPoller = new PeriodicDataPoller
( fetchClusterServicesPollerContext, fetchClusterServicesPollerResponseHandler );
/* Kick the polling loop off. */
fetchClusterServicesPoller.start();