ambari-web/app/controllers/global/cluster_controller.js (399 lines of code) (raw):
/**
* 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.
*/
var App = require('app');
var stringUtils = require('utils/string_utils');
var credentialUtils = require('utils/credentials');
App.ClusterController = Em.Controller.extend(App.ReloadPopupMixin, {
name: 'clusterController',
isLoaded: false,
ambariProperties: null,
clusterEnv: null,
clusterDataLoadedPercent: 'width:0', // 0 to 1
isClusterNameLoaded: false,
isAlertsLoaded: false,
isComponentsStateLoaded: false,
isHostsLoaded: false,
isConfigsPropertiesLoaded: false,
isComponentsConfigLoaded: false,
isStackConfigsLoaded: false,
isServiceMetricsLoaded: false,
/**
* @type {boolean}
*/
isHostComponentMetricsLoaded: false,
isHDFSNameSpacesLoaded: false,
/**
* This counter used as event trigger to notify that quick links should be changed.
*/
quickLinksUpdateCounter: 0,
/**
* Ambari uses custom jdk.
* @type {Boolean}
*/
isCustomJDK: false,
isHostContentLoaded: Em.computed.and('isHostsLoaded', 'isComponentsStateLoaded'),
isServiceContentFullyLoaded: Em.computed.and('isServiceMetricsLoaded', 'isComponentsStateLoaded', 'isComponentsConfigLoaded'),
isStackVersionsLoaded: false,
clusterName: Em.computed.alias('App.clusterName'),
updateLoadStatus: function (item) {
var loadList = this.get('dataLoadList');
var loaded = true;
var numLoaded = 0;
var loadListLength = 0;
loadList.set(item, true);
for (var i in loadList) {
if (loadList.hasOwnProperty(i)) {
loadListLength++;
if (!loadList[i] && loaded) {
loaded = false;
}
}
// calculate the number of true
if (loadList.hasOwnProperty(i) && loadList[i]) {
numLoaded++;
}
}
this.set('isLoaded', loaded);
this.set('clusterDataLoadedPercent', 'width:' + (Math.floor(numLoaded / loadListLength * 100)).toString() + '%');
},
dataLoadList: Em.Object.create({
'stackComponents': false,
'services': false
}),
/**
* load cluster name
*/
loadClusterName: function (reload, deferred) {
var dfd = deferred || $.Deferred();
if (App.get('clusterName') && App.get('clusterId') && !reload) {
App.set('clusterName', this.get('clusterName'));
this.set('isClusterNameLoaded', true);
dfd.resolve();
} else {
App.ajax.send({
name: 'cluster.load_cluster_name',
sender: this,
data: {
reloadPopupText: Em.I18n.t('app.reloadPopup.noClusterName.text'),
errorLogMessage: 'failed on loading cluster name',
callback: this.loadClusterName,
args: [reload, dfd],
shouldUseDefaultHandler: true
},
success: 'reloadSuccessCallback',
error: 'reloadErrorCallback',
callback: function () {
if (!App.get('currentStackVersion')) {
App.set('currentStackVersion', App.defaultStackVersion);
}
}
}).then(
function () {
dfd.resolve();
},
null
);
}
return dfd.promise();
},
reloadSuccessCallback: function (data) {
this._super();
if (data.items && data.items.length > 0) {
App.setProperties({
clusterId: data.items[0].Clusters.cluster_id,
clusterName: data.items[0].Clusters.cluster_name,
currentStackVersion: data.items[0].Clusters.version,
isKerberosEnabled: data.items[0].Clusters.security_type === 'KERBEROS'
});
this.set('isClusterNameLoaded', true);
}
},
setServerClock: function (data) {
var clientClock = new Date().getTime();
var serverClock = (Em.getWithDefault(data, 'RootServiceComponents.server_clock', '')).toString();
serverClock = serverClock.length < 13 ? serverClock + '000' : serverClock;
App.set('clockDistance', serverClock - clientClock);
App.set('currentServerTime', parseInt(serverClock));
},
getServerClockErrorCallback: Em.K,
getUrl: function (testUrl, url) {
return (App.get('testMode')) ? testUrl : App.get('apiPrefix') + '/clusters/' + App.get('clusterName') + url;
},
/**
* load all data and update load status
*/
loadClusterData: function () {
this.loadAuthorizations();
this.getAllHostNames();
if (!App.get('clusterName')) {
return;
}
if (this.get('isLoaded')) { // do not load data repeatedly
App.router.get('mainController').startPolling();
return;
}
App.router.get('userSettingsController').getAllUserSettings();
App.router.get('errorsHandlerController').loadErrorLogs();
this.loadClusterInfo();
this.restoreUpgradeState();
App.router.get('wizardWatcherController').getUser();
this.loadClusterDataToModel();
//force clear filters for hosts page to load all data
App.db.setFilterConditions('mainHostController', null);
},
loadClusterInfo: function() {
var clusterUrl = this.getUrl('/data/clusters/cluster.json', '?fields=Clusters');
App.HttpClient.get(clusterUrl, App.clusterMapper, {
complete: function (jqXHR, textStatus) {
App.set('isCredentialStorePersistent', Em.getWithDefault(App.Cluster.find().findProperty('clusterName', App.get('clusterName')), 'isCredentialStorePersistent', false));
}
}, Em.K);
},
/**
* Order of loading:
* 1. load all created service components
* 2. request for service components supported by stack
* 3. load stack components to model
* 4. request for services
* 5. put services in cache
* 6. request for hosts and host-components (single call)
* 7. request for service metrics
* 8. load host-components to model
* 9. load services from cache with metrics to model
*/
loadClusterDataToModel: function() {
var self = this;
this.loadStackServiceComponents(function (data) {
data.items.forEach(function (service) {
service.StackServices.is_selected = true;
service.StackServices.is_installed = false;
}, self);
App.stackServiceMapper.mapStackServices(data);
App.config.setPreDefinedServiceConfigs(true);
self.updateLoadStatus('stackComponents');
self.loadServicesAndComponents();
});
},
loadServicesAndComponents: function() {
var updater = App.router.get('updateController');
var self = this;
updater.updateServices(function () {
self.updateLoadStatus('services');
//hosts should be loaded after services in order to properly populate host-component relation in App.cache.services
updater.updateHost(function () {
self.set('isHostsLoaded', true);
self.loadAlerts();
});
self.loadConfigProperties();
// components state loading doesn't affect overall progress
updater.updateComponentsState(function () {
self.set('isComponentsStateLoaded', true);
// service metrics should be loaded after components state for mapping service components to service in the DS model
// service metrics loading doesn't affect overall progress
updater.updateServiceMetric(function () {
self.set('isServiceMetricsLoaded', true);
// make second call, because first is light since it doesn't request host-component metrics
updater.updateServiceMetric(function() {
self.set('isHostComponentMetricsLoaded', true);
updater.updateHDFSNameSpaces();
});
// components config loading doesn't affect overall progress
self.loadComponentWithStaleConfigs(function () {
self.set('isComponentsConfigLoaded', true);
});
});
});
});
},
loadComponentWithStaleConfigs: function (callback) {
return App.ajax.send({
name: 'components.get.staleConfigs',
sender: this,
success: 'loadComponentWithStaleConfigsSuccessCallback',
callback: callback
});
},
loadComponentWithStaleConfigsSuccessCallback: function(json) {
json.items.forEach((item) => {
const componentName = item.ServiceComponentInfo.component_name;
const hosts = item.host_components.mapProperty('HostRoles.host_name') || [];
App.componentsStateMapper.updateStaleConfigsHosts(componentName, hosts);
});
},
loadConfigProperties: function() {
var self = this;
App.config.loadConfigsFromStack(App.Service.find().mapProperty('serviceName')).always(function () {
App.config.loadClusterConfigsFromStack().always(function () {
App.router.get('configurationController').updateConfigTags().always(() => {
App.router.get('updateController').updateClusterEnv().always(() => {
self.set('isConfigsPropertiesLoaded', true);
});
});
});
});
},
loadAlerts: function() {
var updater = App.router.get('updateController');
var self = this;
console.time('Overall alerts loading time');
updater.updateAlertGroups(function () {
updater.updateAlertDefinitions(function () {
updater.updateAlertDefinitionSummary(function () {
updater.updateUnhealthyAlertInstances(function () {
console.timeEnd('Overall alerts loading time');
self.set('isAlertsLoaded', true);
});
});
});
});
},
/**
* restore upgrade status from server
* and make call to get latest status from server
* Also loading all upgrades to App.StackUpgradeHistory model
*/
restoreUpgradeState: function () {
var self = this;
return this.getAllUpgrades().done(function (data) {
var upgradeController = App.router.get('mainAdminStackAndUpgradeController');
var allUpgrades = data.items.sortProperty('Upgrade.request_id');
var lastUpgradeData = allUpgrades.pop();
if (lastUpgradeData){
var status = lastUpgradeData.Upgrade.request_status;
var lastUpgradeNotFinished = (self.isSuspendedState(status) || self.isRunningState(status));
if (lastUpgradeNotFinished){
/**
* No need to display history if there is only one running or suspended upgrade.
* Because UI still needs to provide user the option to resume the upgrade via the Upgrade Wizard UI.
* If there is more than one upgrade. Show/Hive the tab based on the status.
*/
var hasFinishedUpgrades = allUpgrades.some(function (item) {
var status = item.Upgrade.request_status;
if (!self.isRunningState(status)){
return true;
}
}, self);
App.set('upgradeHistoryAvailable', hasFinishedUpgrades);
} else {
//There is at least one finished upgrade. Display it.
App.set('upgradeHistoryAvailable', true);
}
} else {
//There is no upgrades at all.
App.set('upgradeHistoryAvailable', false);
}
//completed upgrade shouldn't be restored
if (lastUpgradeData) {
if (lastUpgradeData.Upgrade.request_status !== "COMPLETED") {
upgradeController.restoreLastUpgrade(lastUpgradeData);
}
} else {
upgradeController.initDBProperties();
}
App.stackUpgradeHistoryMapper.map(data);
upgradeController.loadStackVersionsToModel(true).done(function () {
upgradeController.loadCompatibleVersions();
upgradeController.updateCurrentStackVersion();
App.set('stackVersionsAvailable', App.StackVersion.find().content.length > 0);
self.set('isStackVersionsLoaded', true);
});
});
},
isRunningState: function(status){
if (status) {
return "IN_PROGRESS" === status || "PENDING" === status || status.contains("HOLDING");
} else {
//init state
return true;
}
},
/**
* ABORTED should be handled as SUSPENDED for the lastUpgradeItem
* */
isSuspendedState: function(status){
return "ABORTED" === status;
},
requestHosts: function (realUrl, callback) {
var testHostUrl = '/data/hosts/HDP2/hosts.json';
var url = this.getUrl(testHostUrl, realUrl);
App.HttpClient.get(url, App.hostsMapper, {
complete: callback
}, callback)
},
/**
*
* @param callback
* @returns {?object}
*/
loadStackServiceComponents: function (callback) {
var callbackObj = {
loadStackServiceComponentsSuccess: callback
};
return App.ajax.send({
name: 'wizard.service_components',
data: {
stackUrl: App.get('stackVersionURL'),
stackVersion: App.get('currentStackVersionNumber')
},
sender: callbackObj,
success: 'loadStackServiceComponentsSuccess'
});
},
loadAmbariProperties: function () {
return App.ajax.send({
name: 'ambari.service',
sender: this,
success: 'loadAmbariPropertiesSuccess',
error: 'loadAmbariPropertiesError'
});
},
loadAuthorizations: function() {
return App.ajax.send({
name: 'router.user.authorizations',
sender: this,
data: {userName: App.db.getLoginName()},
success: 'loadAuthorizationsSuccessCallback'
});
},
loadAuthorizationsSuccessCallback: function(response) {
if (response && response.items) {
App.set('auth', response.items.mapProperty('AuthorizationInfo.authorization_id').uniq());
App.db.setAuth(App.get('auth'));
}
},
loadAmbariPropertiesSuccess: function (data) {
var mainController = App.router.get('mainController');
this.set('ambariProperties', data.RootServiceComponents.properties);
// Absence of 'jdk.name' and 'jce.name' properties says that ambari configured with custom jdk.
this.set('isCustomJDK', App.isEmptyObject(App.permit(data.RootServiceComponents.properties, ['jdk.name', 'jce.name'])));
this.setServerClock(data);
mainController.setAmbariServerVersion.call(mainController, data);
mainController.monitorInactivity();
},
loadAmbariPropertiesError: Em.K,
updateClusterData: function () {
var testUrl = '/data/clusters/HDP2/cluster.json';
var clusterUrl = this.getUrl(testUrl, '?fields=Clusters');
App.HttpClient.get(clusterUrl, App.clusterMapper, {
complete: function () {
}
});
},
/**
*
* @returns {*|Transport|$.ajax|boolean|ServerResponse}
*/
getAllHostNames: function () {
return App.ajax.send({
name: 'hosts.all',
sender: this,
success: 'getHostNamesSuccess',
error: 'getHostNamesError'
});
},
getHostNamesSuccess: function (data) {
App.set("allHostNames", data.items.mapProperty("Hosts.host_name"));
},
getHostNamesError: Em.K,
/**
* puts kerberos admin credentials in the live cluster session
* and resend ajax request
* @param {credentialResourceObject} credentialResource
* @param {object} ajaxOpt
* @returns {$.ajax}
*/
createKerberosAdminSession: function (credentialResource, ajaxOpt) {
return credentialUtils.createOrUpdateCredentials(App.get('clusterName'), credentialUtils.ALIAS.KDC_CREDENTIALS, credentialResource).then(function() {
if (ajaxOpt) {
$.ajax(ajaxOpt);
}
});
},
//TODO Replace this check with any other which is applicable to non-HDP stack
/**
* Check if HDP stack version is more or equal than 2.2.2 to determine if pluggable metrics for Storm are supported
* @method checkDetailedRepoVersion
* @returns {promise|*|promise|promise|HTMLElement|promise}
*/
checkDetailedRepoVersion: function () {
var dfd;
var currentStackName = App.get('currentStackName');
var currentStackVersionNumber = App.get('currentStackVersionNumber');
if (currentStackName == 'HDP' && currentStackVersionNumber == '2.2') {
dfd = App.ajax.send({
name: 'cluster.load_detailed_repo_version',
sender: this,
success: 'checkDetailedRepoVersionSuccessCallback',
error: 'checkDetailedRepoVersionErrorCallback'
});
} else {
dfd = $.Deferred();
App.set('isStormMetricsSupported', currentStackName != 'HDP' || stringUtils.compareVersions(currentStackVersionNumber, '2.2') == 1);
dfd.resolve();
}
return dfd.promise();
},
checkDetailedRepoVersionSuccessCallback: function (data) {
var rv = (Em.getWithDefault(data, 'items', []) || []).filter(function(i) {
return Em.getWithDefault(i || {}, 'ClusterStackVersions.stack', null) === App.get('currentStackName') &&
Em.getWithDefault(i || {}, 'ClusterStackVersions.version', null) === App.get('currentStackVersionNumber');
})[0];
var version = Em.getWithDefault(rv || {}, 'repository_versions.0.RepositoryVersions.repository_version', false);
App.set('isStormMetricsSupported', stringUtils.compareVersions(version, '2.2.2') > -1 || !version);
},
checkDetailedRepoVersionErrorCallback: function () {
App.set('isStormMetricsSupported', true);
},
/**
* Load required data for all upgrades from API
* @returns {$.ajax}
*/
getAllUpgrades: function () {
return App.ajax.send({
name: 'cluster.load_last_upgrade',
sender: this
});
},
triggerQuickLinksUpdate: function() {
this.incrementProperty('quickLinksUpdateCounter');
}
});