blob: e2c5fa675f26678955e778b3335208ac0033b585 [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.
*/
var stringUtils = require('utils/string_utils');
var numberUtils = require('utils/number_utils');
var blueprintUtils = require('utils/blueprint');
App.AssignMasterOnStep7Controller = Em.Controller.extend(App.BlueprintMixin, App.AssignMasterComponents, {
name: "assignMasterOnStep7Controller",
useServerValidation: false,
showInstalledMastersFirst: false,
configWidgetContext: {},
configActionComponent: {},
content: function () {
return this.get('configWidgetContext.controller.content') || {};
}.property('configWidgetContext.controller.content'),
popup: null,
mastersToCreate: [],
markSavedComponentsAsInstalled: true,
/**
* Array of master component names, that should be addable
* Are used in HA wizards to add components, that are not addable for other wizards
* @type {Array}
* @override
*/
mastersAddableInHA: Em.computed.alias('App.components.isMasterAddableOnlyOnHA'),
/**
* Marks component add/delete action to be performed ahead.
* @param context {Object} Context of the calling function
* @param action {String} ADD|DELETE
* @param hostComponent {Object}
* @public
* @method {execute}
*/
execute: function (context, action, hostComponent) {
this.set('configWidgetContext', context);
this.set('content', context.get('controller.content'));
this.set('configActionComponent', hostComponent);
var missingDependentServices = this.getAllMissingDependentServices();
var isNonWizardPage = !this.get('content.controllerName');
switch (action) {
case 'ADD':
if (missingDependentServices.length && isNonWizardPage) {
this.showInstallServicesPopup(missingDependentServices);
} else {
this.set('mastersToCreate', [hostComponent.componentName]);
this.showAssignComponentPopup();
}
break;
case 'DELETE':
this.set('mastersToCreate', [hostComponent.componentName]);
this.removeMasterComponent();
break;
}
},
/**
* Used to set showAddControl/showRemoveControl flag
* @param componentName
* @override
*/
updateComponent: function(componentName) {
this._super(componentName);
if (!this.get('mastersToCreate').contains(componentName)) {
this.get("selectedServicesMasters").filterProperty("component_name", componentName).forEach(function(c) {
c.set('showAddControl', false);
c.set('showRemoveControl', false);
});
}
},
/**
* Assign Master page will be displayed in the popup
* @private
* @method
*/
showAssignComponentPopup: function () {
var self = this;
// Master component hosts should be loaded only when content.controller name is not defined i.e non-wizard pages
if (!this.get('content.controllerName')) {
this.loadMasterComponentHosts();
}
var mastersToCreate = this.get('mastersToCreate');
var masterToCreateDisplayName = App.format.role(mastersToCreate[0]);
var configWidgetContext = this.get('configWidgetContext');
var config = this.get('configWidgetContext.config');
var popup = App.ModalPopup.show({
classNames: ['full-width-modal', 'add-service-wizard-modal'],
header: Em.I18n.t('assign.master.popup.header').format(masterToCreateDisplayName),
bodyClass: App.AssignMasterOnStep7View.extend({
controller: self
}),
primary: Em.I18n.t('form.cancel'),
showFooter: false,
onClose: function () {
this.showWarningPopup();
},
showWarningPopup: function() {
var mainPopupContext = this;
App.ModalPopup.show({
encodeBody: false,
header: Em.I18n.t('common.warning'),
primaryClass: 'btn-warning',
body: Em.I18n.t('assign.master.popup.cancel.body').format(masterToCreateDisplayName),
onPrimary: function () {
configWidgetContext.toggleProperty('controller.forceUpdateBoundaries');
var value = config.get('initialValue');
config.set('value', value);
configWidgetContext.setValue(value);
configWidgetContext.sendRequestRorDependentConfigs(config);
this.hide();
mainPopupContext.hide();
}
});
},
didInsertElement: function () {
this._super();
this.fitHeight();
self.set('configWidgetContext.controller.saveInProgress', false);
}
});
this.set('popup', popup);
},
/**
* Displays the popup to install required service dependencies for being added component with this config change
* @param missingDependentServices {String[]} Array of service display names
*/
showInstallServicesPopup: function (missingDependentServices) {
var displayServices = stringUtils.getFormattedStringFromArray(missingDependentServices);
var configWidgetContext = this.get('configWidgetContext');
var config = this.get('configWidgetContext.config');
var configDisplayName = config.get('displayName').toLowerCase();
return App.ModalPopup.show({
header: Em.I18n.t('installer.step7.missing.service.header'),
body: Em.I18n.t('installer.step7.missing.service.body').format(displayServices, configDisplayName),
primaryClass: 'btn-danger',
onPrimary: function () {
configWidgetContext.toggleProperty('controller.forceUpdateBoundaries');
var value = config.get('initialValue');
config.set('value', value);
configWidgetContext.setValue(value);
configWidgetContext.sendRequestRorDependentConfigs(config);
this._super();
},
secondary: null,
showCloseButton: false,
didInsertElement: function () {
this._super();
configWidgetContext.set('controller.saveInProgress', false);
}
});
},
/**
* This method is used while installing or adding a service
* Removes the masterComponent that was previously being tracked to be added to the cluster
* @private
* @method {removeMasterComponent}
*/
removeMasterComponent: function () {
var componentsToDelete = this.get('mastersToCreate');
var componentsFromConfigs = this.get('content.componentsFromConfigs');
if (this.get('content.controllerName')) {
var masterComponentHosts = this.get('content.masterComponentHosts');
var recommendationsHostGroups = this.get('content.recommendationsHostGroups');
componentsToDelete.forEach(function (_componentName) {
masterComponentHosts = masterComponentHosts.rejectProperty('component', _componentName);
recommendationsHostGroups.blueprint.host_groups.forEach(function(hostGroup){
hostGroup.components = hostGroup.components.rejectProperty('name', _componentName);
}, this);
componentsFromConfigs = componentsFromConfigs.without(_componentName);
}, this);
this.get('content').set('masterComponentHosts', masterComponentHosts);
this.set('content.componentsFromConfigs', componentsFromConfigs);
this.set('content.recommendationsHostGroups', recommendationsHostGroups);
} else {
this.clearComponentsToBeAdded(componentsToDelete[0]);
var hostComponent = App.HostComponent.find().findProperty('componentName', componentsToDelete[0]);
if (hostComponent) {
App.set('componentToBeDeleted', Em.Object.create({
componentName: componentsToDelete[0],
hostName: hostComponent.get('hostName')
}));
}
}
var configActionComponent = this.get('configActionComponent');
this.get('configWidgetContext.config').set('configActionComponent', configActionComponent);
},
/**
* Load active host list to <code>hosts</code> variable
* @override
* @method renderHostInfo
*/
renderHostInfo: function () {
var parentController = this.get('content.controllerName');
if (parentController) {
this._super();
} else {
var hosts = App.Host.find().toArray();
var result = [];
for (var p = 0; p < hosts.length; p++) {
result.push(Em.Object.create({
host_name: hosts[p].get('hostName'),
cpu: hosts[p].get('cpu'),
memory: hosts[p].get('memory'),
maintenance_state: hosts[p].get('maintenance_state'),
disk_info: hosts[p].get('diskInfo'),
host_info: Em.I18n.t('installer.step5.hostInfo').fmt(hosts[p].get('hostName'), numberUtils.bytesToSize(hosts[p].get('memory'), 1, 'parseFloat', 1024), hosts[p].get('cpu'))
}));
}
this.set("hosts", result);
this.sortHosts(result);
}
},
/**
* This method is called on Service->config page and is responsible to load the "Assign Master popup"
* with the installed master component hosts.
* @private
* @method {loadMasterComponentHosts}
*/
loadMasterComponentHosts: function () {
var stackMasterComponents = App.get('components.masters').uniq();
var masterComponentHosts = [];
App.HostComponent.find().filter(function (component) {
return stackMasterComponents.contains(component.get('componentName'));
}).forEach(function (item) {
masterComponentHosts.push({
component: item.get('componentName'),
hostName: item.get('hostName'),
isInstalled: true,
serviceId: item.get('service.id'),
display_name: item.get('displayName')
})
});
this.set("masterComponentHosts", masterComponentHosts);
},
/**
* Returns array of dependent services that are yet not installed in the cluster
* @private
* @method getAllMissingDependentServices
* @return missingDependentServices {Array}
*/
getAllMissingDependentServices: function () {
var configActionComponentName = this.get('configActionComponent').componentName;
var componentStackService = App.StackServiceComponent.find(configActionComponentName).get('stackService');
var dependentServices = componentStackService.get('requiredServices');
return dependentServices.filter(function (item) {
return !App.Service.find().findProperty('serviceName', item);
}).map(function (item) {
return App.StackService.find(item).get('displayName');
});
},
/**
* This method saves masterComponent layout that is used on subsequent "Review" and "Install start and Test services" pages.
* @private
* @method {saveMasterComponentHosts}
*/
saveMasterComponentHosts: function() {
var controller = App.router.get(this.get('content.controllerName'));
var componentsFromConfigs = this.get('content.componentsFromConfigs');
controller.saveMasterComponentHosts(this, true);
controller.loadMasterComponentHosts(true);
componentsFromConfigs = componentsFromConfigs.concat(this.get('mastersToCreate'));
this.set('content.componentsFromConfigs', componentsFromConfigs);
},
/**
* This method saves host group layout that is used for blueprint validation call made while transitioning to "Review" page.
* @private
* @method {saveRecommendationsHostGroups}
*/
saveRecommendationsHostGroups: function() {
var recommendationsHostGroups = this.get('content.recommendationsHostGroups');
var mastersToCreate = this.get('mastersToCreate');
mastersToCreate.forEach(function(componentName) {
var hostName = this.getSelectedHostName(componentName);
if (hostName && recommendationsHostGroups) {
var hostGroups = recommendationsHostGroups.blueprint_cluster_binding.host_groups;
var isHostPresent = false;
var i = 0;
while (i < hostGroups.length) {
var hosts = hostGroups[i].hosts;
isHostPresent = hosts.someProperty('fqdn', hostName);
if (isHostPresent) break;
i++;
}
if (isHostPresent) {
var hostGroupName = hostGroups[i].name;
var hostGroup = recommendationsHostGroups.blueprint.host_groups.findProperty('name', hostGroupName);
var addHostComponentInGroup = !hostGroup.components.someProperty('name', componentName);
if (addHostComponentInGroup) {
hostGroup.components.pushObject({name: componentName});
}
}
}
}, this);
this.set('content.recommendationsHostGroups', recommendationsHostGroups);
},
/**
* Get the fqdn hostname as selected by the user for the component.
* @param componentName
* @return {String}
*/
getSelectedHostName: function(componentName) {
var selectedServicesMasters = this.get('selectedServicesMasters');
return selectedServicesMasters.findProperty('component_name', componentName).selectedHost;
},
/**
* set App.componentToBeAdded to use it on subsequent validation call while saving configuration
* @param componentName {String}
* @param hostName {String}
* @method {setGlobalComponentToBeAdded}
*/
setGlobalComponentToBeAdded: function(componentName, hostName) {
var componentToBeAdded = Em.Object.create({
componentName: componentName,
hostNames: [hostName]
});
App.set('componentToBeAdded', componentToBeAdded);
},
/**
* clear 'componentToBeDeleted' object
* @param componentName {String}
* @public
* @method {clearComponentsToBeDeleted}
*/
clearComponentsToBeDeleted: function(componentName) {
var componentsToBeDeleted = App.get('componentToBeDeleted');
if (!App.isEmptyObject(componentsToBeDeleted) && componentsToBeDeleted.get('componentName') === componentName) {
App.set('componentToBeDeleted', {});
}
},
/**
* clear 'componentToBeAdded' object
* @param componentName {String}
*/
clearComponentsToBeAdded: function(componentName) {
var componentsToBeAdded = App.get('componentToBeAdded');
if (!App.isEmptyObject(componentsToBeAdded) && componentsToBeAdded.get('componentName') === componentName) {
App.set('componentToBeAdded', {});
}
},
/**
* Submit button click handler
* @method submit
*/
submit: function () {
var self = this;
App.get('router.mainAdminKerberosController').getKDCSessionState(function() {
self.get('popup').hide();
var context = self.get('configWidgetContext');
context.toggleProperty('controller.forceUpdateBoundaries');
var configActionComponent = self.get('configActionComponent');
var componentHostName = self.getSelectedHostName(configActionComponent.componentName);
var config = self.get('configWidgetContext.config');
var oldValueKey = context.get('controller.wizardController.name') === 'installerController' ? 'initialValue' : 'savedValue';
// TODO remove after stack advisor is able to handle this case
// workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive from Install Wizard
var serviceConfigs = context.get('controller.stepConfigs').findProperty('serviceName', context.get('controller.selectedService.serviceName')).get('configs');
var dependencies = context.get('config.configAction.dependencies');
if (self.get('content.controllerName')) {
self.saveMasterComponentHosts();
self.saveRecommendationsHostGroups();
// TODO remove after stack advisor is able to handle this case
// workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive from Install Wizard
var miscConfigs = context.get('controller.stepConfigs').findProperty('serviceName', 'MISC').get('configs');
serviceConfigs = serviceConfigs.concat(miscConfigs);
} else {
self.setGlobalComponentToBeAdded(configActionComponent.componentName, componentHostName);
self.clearComponentsToBeDeleted(configActionComponent.componentName);
}
configActionComponent.hostName = componentHostName;
config.set('configActionComponent', configActionComponent);
/* TODO uncomment after stack advisor is able to handle this case
context.get('controller').loadConfigRecommendations([{
type: App.config.getConfigTagFromFileName(config.get('fileName')),
name: config.get('name'),
old_value: config.get(oldValueKey)
}]);
*/
// TODO remove after stack advisor is able to handle this case
// workaround for hadoop.proxyuser.{{hiveUser}}.hosts after adding Hive Server Interactive
if (dependencies) {
var foreignKeys = {};
if (dependencies.foreignKeys) {
dependencies.foreignKeys.forEach(function (dependency) {
var matchingProperty = serviceConfigs.find(function (property) {
return property.get('filename') === App.config.getOriginalFileName(dependency.fileName) && property.get('name') === dependency.propertyName;
});
if (matchingProperty) {
foreignKeys[dependency.key] = matchingProperty.get('value');
}
});
}
if (dependencies.properties && dependencies.initializer) {
var initializer = App.get(dependencies.initializer.name);
var setup = Em.getProperties(foreignKeys, dependencies.initializer.setupKeys);
initializer.setup(setup);
var blueprintObject = {};
dependencies.properties.forEach(function (property) {
var propertyObject = Em.getProperties(property, ['name', 'fileName']);
if (property.nameTemplate) {
var name = property.nameTemplate;
Em.keys(foreignKeys).forEach(function (key) {
name = name.replace('{{' + key + '}}', foreignKeys[key]);
});
propertyObject.name = name;
}
if (!blueprintObject[property.fileName]) {
blueprintObject[property.fileName] = {
properties: {}
};
}
var masterComponents = [];
if (self.get('content.controllerName')) {
var savedMasterComponents = context.get('controller.content.masterComponentHosts').filter(function (componentObject) {
return dependencies.initializer.componentNames.contains(componentObject.component);
});
masterComponents = savedMasterComponents.map(function (componentObject) {
var masterComponent = Em.getProperties(componentObject, ['component', 'hostName']);
masterComponent.isInstalled = true;
return masterComponent;
});
} else {
var hostsMap = blueprintUtils.getComponentForHosts();
Em.keys(hostsMap).forEach(function (hostName) {
hostsMap[hostName].forEach(function (componentName) {
if (dependencies.initializer.componentNames.contains(componentName)) {
masterComponents.push({
component: componentName,
hostName: hostName,
isInstalled: true
});
}
});
});
}
var result = initializer.initialValue(propertyObject, {
masterComponentHosts: masterComponents
});
var propertiesMap = blueprintObject[propertyObject.fileName].properties;
propertiesMap[propertyObject.name] = result.value;
if (property.isHostsList) {
var service = App.config.get('serviceByConfigTypeMap')[propertyObject.fileName];
if (service) {
var serviceName = service.get('serviceName');
var configs = serviceName === context.get('controller.selectedService.serviceName') ? serviceConfigs :
context.get('controller.stepConfigs').findProperty('serviceName', serviceName).get('configs');
var originalFileName = App.config.getOriginalFileName(propertyObject.fileName);
var currentProperty = configs.find(function (configProperty) {
return configProperty.get('filename') === originalFileName && configProperty.get('name') === propertyObject.name;
});
if (currentProperty) {
propertiesMap[propertyObject.name] = currentProperty.get('value');
App.config.updateHostsListValue(propertiesMap, propertyObject.fileName, propertyObject.name, propertyObject.value, property.isHostsArray);
}
}
}
context.get('controller').loadRecommendationsSuccess({
resources: [
{
recommendations: {
blueprint: {
configurations: blueprintObject
}
}
}
]
}, null, {
dataToSend: {
changed_configurations: [{
type: App.config.getConfigTagFromFileName(config.get('fileName')),
name: config.get('name'),
old_value: config.get(oldValueKey)
}]
}
});
initializer.cleanup();
});
}
}
});
},
/**
* function called for onclcik event on cancel button for the popup
*/
onCancel: function() {
this.get('popup').showWarningPopup();
}
});