AMBARI-14501 Improve config recommendations flow 2. (ababiichuk)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/73a850eb Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/73a850eb Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/73a850eb Branch: refs/heads/branch-dev-patch-upgrade Commit: 73a850eb0bb781d3f6650b176d375aca9b5021f2 Parents: ff3643c Author: ababiichuk <[email protected]> Authored: Mon Dec 28 13:01:20 2015 +0200 Committer: ababiichuk <[email protected]> Committed: Mon Dec 28 14:17:03 2015 +0200 ---------------------------------------------------------------------- .../controllers/main/service/info/configs.js | 16 +- .../app/controllers/wizard/step7_controller.js | 86 +++- ambari-web/app/mixins.js | 3 + .../configs/config_recommendation_parser.js | 248 ++++++++++ .../common/configs/config_recommendations.js | 201 ++++++++ ...onfig_with_override_recommendation_parser.js | 113 +++++ .../app/mixins/common/configs/configs_saver.js | 2 +- .../mixins/common/configs/enhanced_configs.js | 464 ++----------------- ambari-web/app/mixins/common/serverValidator.js | 2 +- .../models/configs/objects/service_config.js | 4 + .../configs/objects/service_config_property.js | 14 + ambari-web/app/routes/add_service_routes.js | 2 +- .../modal_popups/dependent_configs_list.hbs | 4 +- ambari-web/app/utils/config.js | 26 +- ambari-web/app/views/common/controls_view.js | 4 +- .../common/modal_popups/select_groups_popup.js | 2 +- .../main/service/info/config_test.js | 4 +- .../common/configs/enhanced_configs_test.js | 10 +- .../configs/widgets/config_widget_view_test.js | 14 +- 19 files changed, 770 insertions(+), 449 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/controllers/main/service/info/configs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js index 1eef9a1..85b4f60 100644 --- a/ambari-web/app/controllers/main/service/info/configs.js +++ b/ambari-web/app/controllers/main/service/info/configs.js @@ -264,7 +264,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A this.get('requestsInProgress').clear(); this.clearLoadInfo(); this.clearSaveInfo(); - this.clearDependentConfigs(); + this.clearAllRecommendations(); this.setProperties({ saveInProgress: false, isInit: true, @@ -536,6 +536,18 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A }, /** + * Allow update property if recommendations + * is based on changing property + * + * @param parentProperties + * @returns {boolean} + * @override + */ + allowUpdateProperty: function(parentProperties) { + return !!(parentProperties && parentProperties.length); + }, + + /** * trigger App.config.createOverride * @param {Object[]} stepConfig * @private @@ -576,7 +588,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A */ doCancel: function () { this.set('preSelectedConfigVersion', null); - this.clearDependentConfigs(); + this.clearAllRecommendations(); this.loadSelectedVersion(this.get('selectedVersion'), this.get('selectedConfigGroup')); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/controllers/wizard/step7_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js index 2726236..823d663 100644 --- a/ambari-web/app/controllers/wizard/step7_controller.js +++ b/ambari-web/app/controllers/wizard/step7_controller.js @@ -706,18 +706,11 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E if (rangerService && !rangerService.get('isInstalled') && !rangerService.get('isSelected')) { App.config.removeRangerConfigs(self.get('stepConfigs')); } - this.loadServerSideConfigsRecommendations().always(function() { - if (self.get('wizardController.name') == 'addServiceController') { - // for Add Service just remove or add dependent properties and ignore config values changes - // for installed services only - self.clearDependenciesForInstalledServices(self.get('installedServiceNames'), self.get('stepConfigs')); - } - self.completeConfigLoading(); - }); + this.loadServerSideConfigsRecommendations().always(this.completeConfigLoading.bind(this)); }, completeConfigLoading: function() { - this.clearDependentConfigsByService(App.StackService.find().filterProperty('isSelected').mapProperty('serviceName')); + this.clearRecommendationsByServiceName(App.StackService.find().filterProperty('isSelected').mapProperty('serviceName')); console.timeEnd('wizard loadStep: '); this.set('isRecommendedLoaded', true); if (this.get('content.skipConfigStep')) { @@ -727,6 +720,17 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E }, /** + * Update initialValues only while loading recommendations first time + * + * @param serviceName + * @returns {boolean} + * @override + */ + updateInitialOnRecommendations: function(serviceName) { + return this._super(serviceName) && !this.get('isRecommendedLoaded'); + }, + + /** * Mark descriptor properties in configuration object. * * @param {Object[]} configs - config properties to change @@ -1224,6 +1228,70 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E }, /** + * @param serviceName + * @returns {boolean} + * @override + */ + useInitialValue: function(serviceName) { + return !App.Service.find(serviceName).get('serviceName', serviceName); + }, + + /** + * + * @param parentProperties + * @param name + * @param fileName + * @returns {*} + * @override + */ + allowUpdateProperty: function(parentProperties, name, fileName) { + if (['installerController'].contains(this.get('wizardController.name')) || !!(parentProperties && parentProperties.length)) { + return true; + } else if (['addServiceController'].contains(this.get('wizardController.name'))) { + var stackProperty = App.configsCollection.getConfigByName(name, fileName); + if (!stackProperty || !this.get('installedServices')[stackProperty.serviceName]) { + return true; + } else if (stackProperty.propertyDependsOn.length) { + return stackProperty.propertyDependsOn.filter(function (p) { + var service = App.config.getServiceByConfigType(p.type); + return service && !this.get('installedServices')[service.get('serviceName')]; + }, this).length; + } else { + return false; + } + } + return true; + }, + + /** + * remove config based on recommendations + * @param config + * @param configsCollection + * @param parentProperties + * @protected + * @override + */ + _removeConfigByRecommendation: function (config, configsCollection, parentProperties) { + this._super(config, configsCollection, parentProperties); + /** + * need to update wizard info when removing configs for installed services; + */ + var installedServices = this.get('installedServices'), wizardController = this.get('wizardController'), + fileNamesToUpdate = wizardController ? wizardController.getDBProperty('fileNamesToUpdate') || [] : [], + fileName = Em.get(config, 'filename'), serviceName = Em.get(config, 'serviceName'); + var modifiedFileNames = this.get('modifiedFileNames'); + if (modifiedFileNames && !modifiedFileNames.contains(fileName)) { + modifiedFileNames.push(fileName); + } else if (wizardController && installedServices[serviceName]) { + if (!fileNamesToUpdate.contains(fileName)) { + fileNamesToUpdate.push(fileName); + } + } + if (wizardController) { + wizardController.setDBProperty('fileNamesToUpdate', fileNamesToUpdate.uniq()); + } + }, + /** * @method manageConfigurationGroup */ manageConfigurationGroup: function () { http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins.js b/ambari-web/app/mixins.js index 90fbaa2..38d112d 100644 --- a/ambari-web/app/mixins.js +++ b/ambari-web/app/mixins.js @@ -45,6 +45,9 @@ require('mixins/wizard/selectHost'); require('mixins/wizard/addSecurityConfigs'); require('mixins/wizard/wizard_menu_view'); require('mixins/wizard/assign_master_components'); +require('mixins/common/configs/config_recommendations'); +require('mixins/common/configs/config_recommendation_parser'); +require('mixins/common/configs/config_with_override_recommendation_parser'); require('mixins/common/configs/enhanced_configs'); require('mixins/common/configs/configs_saver'); require('mixins/common/configs/configs_loader'); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins/common/configs/config_recommendation_parser.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/config_recommendation_parser.js b/ambari-web/app/mixins/common/configs/config_recommendation_parser.js new file mode 100644 index 0000000..4da42dd --- /dev/null +++ b/ambari-web/app/mixins/common/configs/config_recommendation_parser.js @@ -0,0 +1,248 @@ +/** + * 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 validator = require('utils/validator'); + +App.ConfigRecommendationParser = Em.Mixin.create(App.ConfigRecommendations, { + + stepConfigs: [], + + /** + * Method that goes through all configs + * and apply recommendations using callbacks + * + * @param recommendationObject + * @param configs + * @param parentProperties + * @param configGroup + * @param updateCallback + * @param removeCallback + * @param updateBoundariesCallback + */ + parseRecommendations: function(recommendationObject, configs, parentProperties, configGroup, + updateCallback, removeCallback, updateBoundariesCallback) { + var propertiesToDelete = []; + configs.forEach(function (config) { + var name = Em.get(config, 'name'), fileName = Em.get(config, 'filename'), + site = App.config.getConfigTagFromFileName(fileName); + if (recommendationObject[site]) { + var properties = recommendationObject[site].properties, + property_attributes = recommendationObject[site].property_attributes; + if (properties) { + var recommendedValue = App.config.formatValue(properties[name]); + if (!Em.isNone(recommendedValue)) { + /** update config **/ + updateCallback(config, recommendedValue, parentProperties, configGroup); + + delete recommendationObject[site].properties[name]; + } + } + if (property_attributes) { + var propertyAttributes = property_attributes[name]; + var stackProperty = App.configsCollection.getConfigByName(name, fileName); + for (var attr in propertyAttributes) { + if (attr == 'delete' && this.allowUpdateProperty(parentProperties, name, fileName)) { + propertiesToDelete.push(config); + } else if (stackProperty) { + /** update config boundaries **/ + updateBoundariesCallback(stackProperty, attr, propertyAttributes[attr], configGroup); + } + } + } + } + }, this); + + if (propertiesToDelete.length) { + propertiesToDelete.forEach(function (property) { + /** remove config **/ + removeCallback(property, configs, parentProperties, configGroup); + + }, this); + } + }, + + /** + * Method that goes through all configs + * and apply recommendations to configs when it's needed + * + * @param {Object} recommendationObject + * @param {Object[]} configs + * @param {Object[]} parentProperties + */ + updateConfigsByRecommendations: function (recommendationObject, configs, parentProperties) { + this.parseRecommendations(recommendationObject, configs, parentProperties, null, + this._updateConfigByRecommendation.bind(this), this._removeConfigByRecommendation.bind(this), this._updateBoundaries.bind(this)); + }, + + /** + * This method goes through all config recommendations + * and trying to add new properties + * + * @param {Object} recommendationObject + * @param {Object[]} parentProperties + */ + addByRecommendations: function (recommendationObject, parentProperties) { + for (var site in recommendationObject) { + if (Object.keys(recommendationObject[site].properties).length) { + var stepConfig = App.config.getStepConfigForProperty(this.get('stepConfigs'), site), configs = []; + if (stepConfig) { + for (var propertyName in recommendationObject[site].properties) { + if (this.allowUpdateProperty(parentProperties, propertyName, site)) { + this._addConfigByRecommendation(configs, propertyName, site, recommendationObject[site].properties[propertyName], parentProperties); + } + } + var mergedConfigs = configs.concat(stepConfig.get('configs')); + stepConfig.set('configs', mergedConfigs); + } + } + } + }, + + /** + * Update config based on recommendations + * + * @param config + * @param recommendedValue + * @param parentProperties + * @protected + */ + _updateConfigByRecommendation: function (config, recommendedValue, parentProperties) { + Em.assert('config should be defined', config); + Em.set(config, 'recommendedValue', recommendedValue); + if (this.allowUpdateProperty(parentProperties, Em.get(config, 'name'), Em.get(config, 'filename'))) { + Em.set(config, 'value', recommendedValue); + this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), Em.get(config, 'group.name'), recommendedValue, this._getInitialValue(config), parentProperties); + } + if (this.updateInitialOnRecommendations(Em.get(config, 'serviceName'))) { + Em.set(config, 'initialValue', recommendedValue); + } + }, + + /** + * Add config based on recommendations + * + * @param configs + * @param name + * @param fileName + * @param recommendedValue + * @param parentProperties + * @protected + */ + _addConfigByRecommendation: function (configs, name, fileName, recommendedValue, parentProperties) { + fileName = App.config.getOriginalFileName(fileName); + var stackConfig = App.configsCollection.getConfigByName(name, fileName), + service = App.config.get('serviceByConfigTypeMap')[App.config.getConfigTagFromFileName(fileName)]; + if (service) { + var serviceName = stackConfig ? stackConfig.serviceName : service && service.get('serviceName'), + popupProperty = this.getRecommendation(name, fileName), + initialValue = popupProperty ? popupProperty.value : null; + + var coreObject = { + "value": recommendedValue, + "recommendedValue": recommendedValue, + "initialValue": this.updateInitialOnRecommendations(serviceName) ? recommendedValue : initialValue, + "savedValue": !this.useInitialValue(serviceName) && !Em.isNone(initialValue) ? initialValue : null + }; + var addedProperty = stackConfig || App.config.createDefaultConfig(name, serviceName, fileName, false); + Em.setProperties(addedProperty, coreObject); + var addedPropertyObject = App.ServiceConfigProperty.create(addedProperty); + configs.pushObject(addedPropertyObject); + addedPropertyObject.validate(); + + this.applyRecommendation(name, fileName, "Default", + recommendedValue, null, parentProperties); + } + }, + + /** + * Remove config based on recommendations + * + * @param config + * @param configsCollection + * @param parentProperties + * @protected + */ + _removeConfigByRecommendation: function (config, configsCollection, parentProperties) { + Em.assert('config and configsCollection should be defined', config && configsCollection); + configsCollection.removeObject(config); + + this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), Em.get(config, 'group.name'), + null, this._getInitialValue(config), parentProperties); + }, + + /** + * Update config valueAttributes by recommendations + * + * @param {Object} stackProperty + * @param {string} attr + * @param {Number|String|Boolean} value + * @protected + */ + _updateBoundaries: function(stackProperty, attr, value) { + Em.set(stackProperty.valueAttributes, attr, value); + }, + + /** + * Get default config value + * <code>savedValue<code> for installed services + * <code>initialValue<code> for new services + * + * @param configProperty + * @returns {*} + * @protected + */ + _getInitialValue: function (configProperty) { + if (!configProperty) return null; + return this.useInitialValue(Em.get(configProperty, 'serviceName')) ? + Em.get(configProperty, 'initialValue') : Em.get(configProperty, 'savedValue'); + }, + + /** + * Update initial only when <code>initialValue<code> is used + * + * @param {string} serviceName + * @returns {boolean} + */ + updateInitialOnRecommendations: function(serviceName) { + return this.useInitialValue(serviceName); + }, + + /** + * Defines if initialValue of config can be used on current controller + * if not savedValue is used instead + * + * @param {String} serviceName + * @return {boolean} + */ + useInitialValue: function (serviceName) { + return false; + }, + + /** + * Defines if recommendation allowed to be applied + * + * @param parentProperties + * @param name + * @param fileName + * @returns {boolean} + */ + allowUpdateProperty: function (parentProperties, name, fileName) { + return true; + } +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins/common/configs/config_recommendations.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/config_recommendations.js b/ambari-web/app/mixins/common/configs/config_recommendations.js new file mode 100644 index 0000000..583e86d --- /dev/null +++ b/ambari-web/app/mixins/common/configs/config_recommendations.js @@ -0,0 +1,201 @@ +/** + * 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'); + +/** + * @typedef {object} recommendation + * @property {boolean} saveRecommended - by default is true (checkbox binding) + * @property {boolean} saveRecommendedDefault - used for cancel operation to restore previous state (saved checkbox value) + * @property {boolean} isDeleted - true if property was deleted + * @property {boolean} notDefined - true if property was added + * @property {string} propertyName + * @property {string} propertyFileName - file name without '.xml' + * @property {string} configGroup - name of config group, by default "Default" + * @property {string} serviceName + * @property {string} serviceDisplayName + * @property {string} initialValue + * @property {string} recommendedValue + * @property {boolean} allowChangeGroup - flag that allows to change config group for config from dependent not default group + * @property {string[]} parentConfigs - list of properties based on which current recommendation was performed + */ + +App.ConfigRecommendations = Em.Mixin.create({ + + /** + * List of recommendations that was applied to configs + * + * @type {recommendation[]} + */ + recommendations: [], + + /** + * Update recommendation property if exists + * otherwise add new + * + * @param {string} name + * @param {string} fileName + * @param {string} configGroupName + * @param {string} recommendedValue + * @param {string} initialValue + * @param {Object[]}parentProperties + * @returns {recommendation} + */ + applyRecommendation: function(name, fileName, configGroupName, recommendedValue, initialValue, parentProperties) { + var parentPropertiesNames = parentProperties ? parentProperties.map(function(p) { + return App.config.configId(p.name, p.type); + }) : []; + var updated = this.updateRecommendation(name, fileName, configGroupName, recommendedValue, parentPropertiesNames); + if (!updated) + var added = this.addRecommendation(name, fileName, configGroupName, recommendedValue, initialValue, parentPropertiesNames); + return updated || added; + }, + + /** + * Add new recommendation + * + * @param {string} name + * @param {string} fileName + * @param {string} configGroupName + * @param {string} recommendedValue + * @param {string} initialValue + * @param {string[]} parentPropertiesNames + * @returns {recommendation} + */ + addRecommendation: function(name, fileName, configGroupName, recommendedValue, initialValue, parentPropertiesNames) { + Em.assert('name and fileName should be defined', name && fileName); + var site = App.config.getConfigTagFromFileName(fileName); + var service = App.config.get('serviceByConfigTypeMap')[site]; + var recommendation = { + saveRecommended: true, + saveRecommendedDefault: true, + propertyFileName: site, + propertyName: name, + + isDeleted: Em.isNone(recommendedValue), + notDefined: Em.isNone(initialValue), + + configGroup: configGroupName || "Default", + initialValue: initialValue, + parentConfigs: parentPropertiesNames || [], + serviceName: service.get('serviceName'), + allowChangeGroup: false,//TODO groupName!= "Default" && (service.get('serviceName') != this.get('selectedService.serviceName')) + //TODO&& (App.ServiceConfigGroup.find().filterProperty('serviceName', service.get('serviceName')).length > 1), //TODO + serviceDisplayName: service.get('displayName'), + recommendedValue: recommendedValue + }; + this.get('recommendations').pushObject(recommendation); + return recommendation; + }, + + /** + * Remove recommendation + * based on unique identifiers + * + * @param {string} name + * @param {string} fileName + * @param {string} configGroupName + */ + removeRecommendation: function(name, fileName, configGroupName) { + this.removeRecommendationObject(this.getRecommendation(name, fileName, configGroupName)); + }, + + /** + * Remove recommended Object + * + * @param {recommendation} recommendation + */ + removeRecommendationObject: function(recommendation) { + if (recommendation) + this.get('recommendations').removeObject(recommendation); + }, + + /** + * Update recommended object + * + * @param name + * @param fileName + * @param configGroupName + * @param recommendedValue + * @param parentPropertiesNames + * @returns {*|recommendation|null} + */ + updateRecommendation: function(name, fileName, configGroupName, recommendedValue, parentPropertiesNames) { + var recommendation = this.getRecommendation(name, fileName, configGroupName); + if (recommendation) { + Em.set(recommendation, 'recommendedValue', recommendedValue); + if (parentPropertiesNames && parentPropertiesNames.length) { + var mergedProperties = parentPropertiesNames.concat(Em.get(recommendation, 'parentPropertiesNames')); + Em.set(recommendation, 'parentPropertiesNames', mergedProperties); + } + } + return recommendation; + }, + + /** + * Get single recommendation + * + * @param name + * @param fileName + * @param configGroupName + * @returns {recommendation|null} + */ + getRecommendation: function(name, fileName, configGroupName) { + Em.assert('name and fileName should be defined', name && fileName); + return this.get('recommendations').find(function (dcv) { + return dcv.propertyName === name + && dcv.propertyFileName === App.config.getConfigTagFromFileName(fileName) + && dcv.configGroup === (configGroupName || "Default"); + }); + }, + + /** + * Clear recommendations that are + * same as initial value + * + * @method cleanUpRecommendations + */ + cleanUpRecommendations: function() { + var cleanDependentList = this.get('recommendations').filter(function(d) { + return !((Em.isNone(d.initialValue) && Em.isNone(d.recommendedValue)) || d.initialValue == d.recommendedValue); + }, this); + this.set('recommendations', cleanDependentList); + }, + + /** + * Remove all recommendations + * + * @method clearAllRecommendations + */ + clearAllRecommendations: function() { + this.set('recommendations', []); + }, + + /** + * Clear values for dependent configs for given services + * + * @method clearRecommendationsByServiceName + */ + clearRecommendationsByServiceName: function(serviceNames) { + var filteredRecommendations = this.get('recommendations').reject(function(c) { + return serviceNames.contains(c.serviceName); + }, this); + this.set('recommendations', filteredRecommendations); + } + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js b/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js new file mode 100644 index 0000000..5339637 --- /dev/null +++ b/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js @@ -0,0 +1,113 @@ +/** + * 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'); + +App.ConfigWithOverrideRecommendationParser = Em.Mixin.create(App.ConfigRecommendationParser, { + + /** + * Method that goes through all configs + * and apply recommendations to overrides when it's needed + * + * @param {Object} recommendationObject + * @param {Object[]} configs + * @param {Object[]} parentProperties + * @param {App.ServiceConfigGroup} configGroup + */ + updateOverridesByRecommendations: function (recommendationObject, configs, parentProperties, configGroup) { + Em.assert('Config groups should be defined and not default', configGroup && configGroup.get('name') && !configGroup.get('isDefault')); + this.parseRecommendations(recommendationObject, configs, parentProperties, configGroup, + this._updateOverride.bind(this), this._removeOverride.bind(this), this._updateOverrideBoundaries.bind(this)); + }, + + /** + * Update override by recommendations + * includes add/update actions + * + * @param config + * @param recommendedValue + * @param parentProperties + * @param configGroup + * @protected + */ + _updateOverride: function(config, recommendedValue, parentProperties, configGroup) { + debugger; + var updateValue = this.allowUpdateProperty(parentProperties, Em.get(config, 'name'), Em.get(config, 'filename')); + var override = config.getOverride(configGroup.get('name')); + if (override) { + this._updateConfigByRecommendation(override, recommendedValue, parentProperties); + } else if (updateValue) { + this._addConfigOverrideRecommendation(config, recommendedValue, parentProperties, configGroup); + } + }, + + /** + * Remove override by recommendations + * + * @param property + * @param configs + * @param parentProperties + * @param configGroup + * @protected + */ + _removeOverride: function(property, configs, parentProperties, configGroup) { + this._removeConfigByRecommendation(property.getOverride(configGroup.get('name')), property.get('overrides') || [], parentProperties); + }, + + /** + * Add override by recommendations + * + * @param config + * @param recommendedValue + * @param configGroup + * @param parentProperties + * @protected + */ + _addConfigOverrideRecommendation: function (config, recommendedValue, parentProperties, configGroup) { + var popupProperty = this.getRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), configGroup.get('name')), + initialValue = popupProperty ? popupProperty.value : null; + var coreObject = { + "value": recommendedValue, + "recommendedValue": recommendedValue, + "initialValue": initialValue, + "savedValue": !this.useInitialValue(Em.get(config, 'serviceName')) && !Em.isNone(initialValue) ? initialValue : null, + "isEditable": true + }; + var override = App.config.createOverride(config, coreObject, configGroup); + configGroup.get('properties').pushObject(override); + + this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), configGroup.get('name'), + recommendedValue, this._getInitialValue(override), parentProperties); + }, + + /** + * Update override valueAttributes by recommendations + * + * @param {Object} stackProperty + * @param {string} attr + * @param {Number|String|Boolean} value + * @param {App.ServiceConfigGroup} configGroup + * @protected + */ + _updateOverrideBoundaries: function(stackProperty, attr, value, configGroup) { + if (!stackProperty.valueAttributes[configGroup.get('name')]) { + stackProperty.valueAttributes[configGroup.get('name')] = {}; + } + Em.set(stackProperty.valueAttributes[configGroup.get('name')], attr, value); + } +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins/common/configs/configs_saver.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/configs_saver.js b/ambari-web/app/mixins/common/configs/configs_saver.js index 09cfd6f..383e4d0 100644 --- a/ambari-web/app/mixins/common/configs/configs_saver.js +++ b/ambari-web/app/mixins/common/configs/configs_saver.js @@ -620,7 +620,7 @@ App.ConfigsSaverMixin = Em.Mixin.create({ App.QuickViewLinks.proto().loadTags(); } this.showSaveConfigsPopup(header, flag, message, messageClass, value, status, urlParams); - this.clearDependentConfigs(); + this.clearAllRecommendations(); }, /** http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins/common/configs/enhanced_configs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js b/ambari-web/app/mixins/common/configs/enhanced_configs.js index 7efcf55..0d3f20a 100644 --- a/ambari-web/app/mixins/common/configs/enhanced_configs.js +++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js @@ -18,9 +18,8 @@ var App = require('app'); var blueprintUtils = require('utils/blueprint'); -var validator = require('utils/validator'); -App.EnhancedConfigsMixin = Em.Mixin.create({ +App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationParser, { /** * this value is used for observing @@ -49,17 +48,6 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ */ isControllerSupportsEnhancedConfigs: Em.computed.existsIn('name', ['wizardStep7Controller','mainServiceInfoConfigsController']), - /** - * defines if initialValue of config can be used on current controller - * if not savedValue is used instead - * @param {String} serviceName - * @return {boolean} - * @method useInitialValue - */ - useInitialValue: function(serviceName) { - return ['wizardStep7Controller'].contains(this.get('name')) && !App.Service.find().findProperty('serviceName', serviceName); - }, - dependenciesGroupMessage: Em.I18n.t('popup.dependent.configs.dependencies.for.groups'), /** * message fro alert box for dependent configs @@ -75,39 +63,15 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ }.property('changedProperties.length'), /** - * values for dependent configs - * @type {Object[]} - * ex: - * { - * saveRecommended: {boolean}, //by default is true (checkbox binding) - * saveRecommendedDefault: {boolean}, used for cancel operation to restore previous state - * toDelete: {boolean} [true], // defines if property should be deleted - * toAdd: {boolean} [false], // defines if property should be added - * isDeleted: {boolean} [true], // defines if property was deleted, but was present in initial configs - * fileName: {string}, //file name without '.xml' - * propertyName: {string}, - * parentConfigs: {string[]} // name of the parent configs - * configGroup: {string}, - * value: {string}, - * serviceName: {string}, - * allowChangeGroup: {boolean}, //used to disable group link for current service - * serviceDisplayName: {string}, - * recommendedValue: {string} - * } - * @private - */ - _dependentConfigValues: Em.A([]), - - /** * dependent properties that was changed by Ambari * @type {Object[]} */ changedProperties: function() { - return this.get('_dependentConfigValues').filter(function(dp) { + return this.get('recommendations').filter(function(dp) { return (this.get('selectedConfigGroup.isDefault') && Em.get(dp, 'configGroup').contains('Default')) || [this.get('selectedConfigGroup.name'), this.get('selectedConfigGroup.dependentConfigGroups') && this.get('selectedConfigGroup.dependentConfigGroups')[Em.get(dp, 'serviceName')]].contains(Em.get(dp, 'configGroup')); }, this); - }.property('[email protected]', 'selectedConfigGroup'), + }.property('[email protected]', '[email protected]', 'selectedConfigGroup'), /** * defines if change dependent group message should be shown @@ -143,67 +107,6 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ /******************************METHODS THAT WORKS WITH DEPENDENT CONFIGS *************************************/ /** - * clear values for dependent configs - * @method clearDependentConfigs - * @private - */ - clearDependentConfigs: function() { - this.setProperties({ - _dependentConfigValues: [] - }); - }, - - /** - * clear values for dependent configs for given services - * @method clearDependentConfigs - * @private - */ - clearDependentConfigsByService: function(serviceNames) { - var cleanDependencies = this.get('_dependentConfigValues').reject(function(c) { - return serviceNames.contains(c.serviceName); - }, this); - this.get('stepConfigs').filter(function(s) { - return serviceNames.contains(s.get('serviceName')); - }).forEach(function(s) { - s.get('configs').setEach('isNotSaved', false); - }); - this.set('_dependentConfigValues', cleanDependencies); - }, - - /** - * Remove configs from <code>_dependentConfigValues</code> which depends between installed services only. - * - * @param {String[]} installedServices - * @param {App.ServiceConfig[]} stepConfigs - */ - clearDependenciesForInstalledServices: function(installedServices, stepConfigs) { - var allConfigs = stepConfigs.mapProperty('configs').filterProperty('length').reduce(function(p, c) { - return p && p.concat(c); - }); - var cleanDependencies = this.get('_dependentConfigValues').reject(function(item) { - if (Em.get(item, 'propertyName').contains('hadoop.proxyuser')) return false; - if (installedServices.contains(Em.get(item, 'serviceName'))) { - var stackProperty = App.configsCollection.getConfigByName(item.propertyName, item.fileName); - var parentConfigs = stackProperty && stackProperty.propertyDependsOn; - if (!parentConfigs || !parentConfigs.length) { - return true; - } - // check that all parent properties from installed service - return !parentConfigs.reject(function(parentConfig) { - var property = allConfigs.filterProperty('filename', App.config.getOriginalFileName(parentConfig.type)) - .findProperty('name', parentConfig.name); - if (!property) { - return false; - } - return installedServices.contains(Em.get(property, 'serviceName')); - }).length; - } - return false; - }); - this.set('_dependentConfigValues', cleanDependencies); - }, - - /** * get config group object for current service * @param serviceName * @returns {App.ConfigGroup|null} @@ -236,9 +139,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ * @method removeCurrentFromDependentList */ removeCurrentFromDependentList: function (config, saveRecommended) { - var current = this.get('_dependentConfigValues').find(function(dependentConfig) { - return Em.get(dependentConfig, 'propertyName') == config.get('name') && Em.get(dependentConfig, 'fileName') == App.config.getConfigTagFromFileName(config.get('filename')); - }); + var current = this.getRecommendation(config.get('name'), config.get('filename'), config.get('group.name')); if (current) { Em.setProperties(current, { 'saveRecommended': !!saveRecommended, @@ -265,7 +166,8 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ hosts: this.get('hostNames'), services: this.get('serviceNames') }; - var clearConfigsOnAddService = this.isConfigHasInitialState(); + + var clearConfigsOnAddService = configGroup.get('isDefault') && this.isConfigHasInitialState(); if (clearConfigsOnAddService) { recommendations.blueprint.configurations = this.get('initialConfigValues'); } else { @@ -287,7 +189,6 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ stackVersionUrl: App.get('stackVersionURL'), dataToSend: dataToSend, notDefaultGroup: configGroup && !configGroup.get('isDefault'), - initial: initial, clearConfigsOnAddService: clearConfigsOnAddService }, success: 'dependenciesSuccess', @@ -313,7 +214,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ return !this.get('stepConfigs').filter(function(stepConfig) { return stepConfig.get('changedConfigProperties').filter(function(c) { return !this.get('changedProperties').map(function(changed) { - return App.config.configId(changed.propertyName, changed.fileName); + return App.config.configId(changed.propertyName, changed.propertyFileName); }).contains(App.config.configId(c.get('name'), c.get('filename'))); }, this).length; }, this).length; @@ -326,18 +227,16 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ clearConfigValues: function() { this.get('stepConfigs').forEach(function(stepConfig) { stepConfig.get('changedConfigProperties').forEach(function(c) { - var recommendedProperty = this.get('_dependentConfigValues').find(function(d) { - return App.config.configId(d.propertyName, d.fileName) == App.config.configId(c.get('name'), c.get('filename')); - }); + var recommendedProperty = this.getRecommendation(c.get('name'), c.get('filename'), c.get('group.name')); if (recommendedProperty) { - var initialValue = recommendedProperty.value; + var initialValue = recommendedProperty.initialValue; if (Em.isNone(initialValue)) { stepConfig.get('configs').removeObject(c); } else { + c.set('initialValue', initialValue); c.set('value', initialValue); - c.set('recommendedValue', initialValue); } - this.get('_dependentConfigValues').removeObject(recommendedProperty); + this.removeRecommendationObject(recommendedProperty); } }, this) }, this); @@ -379,13 +278,11 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ * @method dependenciesSuccess */ dependenciesSuccess: function (data, opt, params) { - this._saveRecommendedValues(data, params.initial, params.dataToSend.changed_configurations, params.notDefaultGroup); + this._saveRecommendedValues(data, params.dataToSend.changed_configurations, params.notDefaultGroup); this.set("recommendationsConfigs", Em.get(data.resources[0] , "recommendations.blueprint.configurations")); if (params.clearConfigsOnAddService) { - if (this.get('wizardController.name') == 'addServiceController') { - this.clearDependenciesForInstalledServices(this.get('installedServiceNames'), this.get('stepConfigs')); - } this.clearConfigValues(); + this.clearAllRecommendations(); } this.set('recommendationTimeStamp', (new Date).getTime()); }, @@ -395,7 +292,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ * @method showChangedDependentConfigs */ showChangedDependentConfigs: function(event, callback, secondary) { - if (this.get('_dependentConfigValues.length') > 0) { + if (this.get('recommendations.length') > 0) { App.showDependentConfigsPopup(this.get('changedProperties'), this.onSaveRecommendedPopup.bind(this), secondary); } else { if (callback) { @@ -413,23 +310,32 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ */ undoRedoRecommended: function(propertiesToUpdate, redo) { propertiesToUpdate.forEach(function(p) { - var initial = redo ? Em.get(p, 'value') : Em.get(p, 'recommendedValue'); - var recommended = redo ? Em.get(p, 'recommendedValue') : Em.get(p, 'value'); + var initial = redo ? Em.get(p, 'initialValue') : Em.get(p, 'recommendedValue'); + var recommended = redo ? Em.get(p, 'recommendedValue') : Em.get(p, 'initialValue'); var stepConfig = this.get('stepConfigs').findProperty('serviceName', Em.get(p, 'serviceName')); var config = stepConfig.get('configs').find(function(scp) { - return scp.get('name') == Em.get(p, 'propertyName') && scp.get('filename') == Em.get(p, 'fileName'); + return scp.get('name') == Em.get(p, 'propertyName') && scp.get('filename') == App.config.getOriginalFileName(Em.get(p, 'propertyFileName')); }); var selectedGroup = App.ServiceConfigGroup.find().filterProperty('serviceName', Em.get(p, 'serviceName')).findProperty('name', Em.get(p, 'configGroup')); - if (Em.isNone(recommended)) { - if (selectedGroup.get('isDefault')) { + if (selectedGroup.get('isDefault')) { + if (Em.isNone(recommended)) { stepConfig.get('configs').removeObject(config); + } else if (Em.isNone(initial)) { + this._addConfigByRecommendation(stepConfig.get('configs'), Em.get(p, 'propertyName'), Em.get(p, 'propertyFileName'), recommended); } else { - config.get('overrides').removeObject(this._getOverride(config, selectedGroup)); + Em.set(config, 'value', recommended); } - } else if (Em.isNone(initial)) { - this._addConfigByRecommendation(stepConfig, selectedGroup, Em.get(p, 'propertyName'), Em.get(p, 'fileName'), Em.get(p, 'serviceName'), recommended, initial, config); } else { - Em.set(config, 'value', recommended); + if (Em.isNone(recommended)) { + config.get('overrides').removeObject(config.getOverride(selectedGroup.get('name'))); + } else if (Em.isNone(initial)) { + this._addConfigOverrideRecommendation(config, recommended, null, selectedGroup); + } else { + var override = config.getOverride(selectedGroup.get('name')); + if (override) { + override.set('value', recommended); + } + } } }, this); }, @@ -451,7 +357,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ }, this); App.showSelectGroupsPopup(this.get('selectedService.serviceName'), this.get('selectedService.configGroups').findProperty('name', this.get('selectedConfigGroup.name')), - dependentServices, this.get('_dependentConfigValues')) + dependentServices, this.get('recommendations')) }, /** @@ -467,72 +373,33 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ }, /** - * saves values from response for dependent config properties to <code>_dependentConfigValues<code> + * saves values from response for dependent config properties to <code>recommendations<code> * @param data - * @param [updateOnlyBoundaries=false] * @param [changedConfigs=null] * @param notDefaultGroup - * @param updateInitial * @method saveRecommendedValues * @private */ - _saveRecommendedValues: function(data, updateOnlyBoundaries, changedConfigs, notDefaultGroup, updateInitial) { + _saveRecommendedValues: function(data, changedConfigs, notDefaultGroup) { Em.assert('invalid data - `data.resources[0].recommendations.blueprint.configurations` not defined ', data && data.resources[0] && Em.get(data.resources[0], 'recommendations.blueprint.configurations')); var configObject = data.resources[0].recommendations.blueprint.configurations; if (!notDefaultGroup) { - this.parseConfigsByTag(configObject, changedConfigs, updateInitial, updateOnlyBoundaries); - } else if (data.resources[0].recommendations['config-groups']){ + this.get('stepConfigs').forEach(function(stepConfig) { + this.updateConfigsByRecommendations(configObject, stepConfig.get('configs'), changedConfigs); + }, this); + this.addByRecommendations(configObject, changedConfigs); + } else if (data.resources[0].recommendations['config-groups']) { var configFroGroup = data.resources[0].recommendations['config-groups'][0]; - this.parseConfigsByTag(configFroGroup.configurations, changedConfigs, updateInitial, updateOnlyBoundaries); - this.parseConfigsByTag(configFroGroup.dependent_configurations, changedConfigs, updateInitial, updateOnlyBoundaries); - } - this._cleanUpPopupProperties(); - }, - - /** - * saves values from response for dependent configs to <code>_dependentConfigValues<code> - * @param configObject - JSON response from `recommendations` endpoint - * @param {App.ServiceConfigProperty[]} parentConfigs - config properties for which recommendations were received - * @param updateInitial - * @param updateOnlyBoundaries - * @method saveRecommendedValues - * @private - */ - parseConfigsByTag: function(configObject, parentConfigs, updateInitial, updateOnlyBoundaries) { - var parentPropertiesNames = parentConfigs ? parentConfigs.map(function(p) { return App.config.configId(Em.get(p, 'name'), Em.get(p, 'type'))}) : []; - /** get all configs by config group **/ - for (var key in configObject) { - - /** defines main info for file name (service name, config group, config that belongs to filename) **/ - var service = App.config.get('serviceByConfigTypeMap')[key]; - var serviceName = service.get('serviceName'); - var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName); - if (stepConfig) { - var configProperties = stepConfig ? stepConfig.get('configs').filterProperty('filename', App.config.getOriginalFileName(key)) : []; - - var group = this.getGroupForService(serviceName); - - for (var propertyName in configObject[key].properties) { - - var cp = configProperties.findProperty('name', propertyName); - - var configPropertyObject = (!group || group.get('isDefault')) ? cp : this._getOverride(cp, group); - - var recommendedValue = this.getFormattedValue(configObject[key].properties[propertyName]); - var popupProperty = this.getPopupProperty(propertyName, key, Em.get(group || {}, 'name')); - var initialValue = this._getInitialValue(configObject, popupProperty, serviceName, recommendedValue, updateInitial); - if (configPropertyObject) { - this._updateConfigByRecommendation(configPropertyObject, recommendedValue, updateInitial, updateOnlyBoundaries); - } else if (!updateOnlyBoundaries) { - this._addConfigByRecommendation(stepConfig, group, propertyName, key, serviceName, recommendedValue, initialValue, cp); - } - if (!updateOnlyBoundaries) { - this._updatePopup(popupProperty, propertyName, key, recommendedValue, Em.get(configPropertyObject || {}, 'initialValue'), service, Em.get(group || {},'name') || "Default", parentPropertiesNames); - } + this.get('stepConfigs').forEach(function(stepConfig) { + var configGroup = this.getGroupForService(stepConfig.get('serviceName')); + if (configGroup) { + this.updateOverridesByRecommendations(configFroGroup.configurations, stepConfig.get('configs'), changedConfigs, configGroup); + this.updateOverridesByRecommendations(configFroGroup.dependent_configurations, stepConfig.get('configs'), changedConfigs, configGroup); + this.toggleProperty('forceUpdateBoundaries'); } - } + }, this); } - this.parseConfigAttributes(configObject, parentPropertiesNames, updateOnlyBoundaries); + this.cleanUpRecommendations(); }, installedServices: function () { @@ -542,241 +409,6 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ }.property(), /** - * Save property attributes received from recommendations. These attributes are minimum, maximum, - * increment_step. Attributes are stored in <code>App.StackConfigProperty</code> model. - * - * @param {Object[]} configs - * @param parentPropertiesNames - * @param updateOnlyBoundaries - * @private - */ - parseConfigAttributes: function(configs, parentPropertiesNames, updateOnlyBoundaries) { - var self = this; - Em.keys(configs).forEach(function (siteName) { - var fileName = App.config.getOriginalFileName(siteName), - service = App.config.get('serviceByConfigTypeMap')[siteName]; - var serviceName = service && service.get('serviceName'), - stepConfig = self.get('stepConfigs').findProperty('serviceName', serviceName); - if (stepConfig) { - var group = self.getGroupForService(serviceName), - configProperties = stepConfig ? stepConfig.get('configs').filterProperty('filename', App.config.getOriginalFileName(siteName)) : [], - properties = configs[siteName].property_attributes || {}; - Em.keys(properties).forEach(function (propertyName) { - var cp = configProperties.findProperty('name', propertyName); - var stackProperty = App.configsCollection.getConfigByName(propertyName, siteName); - var configObject = (!group || group.get('isDefault')) ? cp : self._getOverride(cp, group); - var configsCollection = !group || group.get('isDefault') ? stepConfig.get('configs') : Em.getWithDefault(cp, 'overrides', []); - var dependentProperty = self.getPopupProperty(propertyName, fileName, Em.get(group || {},'name')); - var attributes = properties[propertyName] || {}; - Em.keys(attributes).forEach(function (attributeName) { - if (attributeName == 'delete' && configObject) { - if (!updateOnlyBoundaries) { - self._removeConfigByRecommendation(configObject, configsCollection); - self._updatePopup(dependentProperty, propertyName, siteName, null, Em.get(configObject, 'initialValue'), service, Em.get(group || {},'name') || "Default", parentPropertiesNames); - } - } else if (stackProperty) { - var selectedConfigGroup = group && !group.get('isDefault') ? group.get('name') : null; - if (selectedConfigGroup) { - if (!stackProperty.valueAttributes[selectedConfigGroup]) { - /** create not default group object for updating such values as min/max **/ - Em.set(stackProperty.valueAttributes, selectedConfigGroup, {}); - } - if (stackProperty.valueAttributes[selectedConfigGroup][attributeName] != attributes[attributeName]) { - Em.set(stackProperty.valueAttributes[selectedConfigGroup], attributeName, attributes[attributeName]); - self.toggleProperty('forceUpdateBoundaries'); - } - } else { - Em.set(stackProperty.valueAttributes, attributeName, attributes[attributeName]); - } - } - }); - }); - } - }); - }, - - /** - * update config based on recommendations - * @param config - * @param recommendedValue - * @param updateInitial - * @param updateOnlyBoundaries - * @private - */ - _updateConfigByRecommendation: function(config, recommendedValue, updateInitial, updateOnlyBoundaries) { - Em.assert('config should be defined', config); - Em.set(config, 'recommendedValue', recommendedValue); - if (!updateOnlyBoundaries) Em.set(config, 'value', recommendedValue); - if (updateInitial && Em.isNone(Em.get(config, 'savedValue'))) Em.set(config, 'initialValue', recommendedValue); - }, - - /** - * remove config based on recommendations - * @param config - * @param configsCollection - * @private - */ - _removeConfigByRecommendation: function(config, configsCollection) { - Em.assert('config and configsCollection should be defined', config && configsCollection); - configsCollection.removeObject(config); - /** - * need to update wizard info when removing configs for installed services; - */ - var installedServices = this.get('installedServices'), wizardController = this.get('wizardController'), - fileNamesToUpdate = wizardController ? wizardController.getDBProperty('fileNamesToUpdate') || [] : [], - fileName = Em.get(config, 'filename'), serviceName = Em.get(config, 'serviceName'); - var modifiedFileNames = this.get('modifiedFileNames'); - if (modifiedFileNames && !modifiedFileNames.contains(fileName)) { - modifiedFileNames.push(fileName); - } else if (wizardController && installedServices[serviceName]) { - if (!fileNamesToUpdate.contains(fileName)) { - fileNamesToUpdate.push(fileName); - } - } - if (wizardController) { - wizardController.setDBProperty('fileNamesToUpdate', fileNamesToUpdate.uniq()); - } - }, - - /** - * add config based on recommendations - * @param stepConfigs - * @param selectedGroup - * @param name - * @param fileName - * @param serviceName - * @param recommendedValue - * @param initialValue - * @param cp - * @private - */ - _addConfigByRecommendation: function(stepConfigs, selectedGroup, name, fileName, serviceName, recommendedValue, initialValue, cp) { - fileName = App.config.getOriginalFileName(fileName); - var coreObject = { - "value": recommendedValue, - "recommendedValue": recommendedValue, - "initialValue": initialValue, - "savedValue": !this.useInitialValue(serviceName) && !Em.isNone(initialValue) ? initialValue : null, - "isEditable": true - }; - if (!selectedGroup || selectedGroup.get('isDefault')) { - var addedProperty = App.configsCollection.getConfigByName(name, fileName) || App.config.createDefaultConfig(name, serviceName, fileName, false, coreObject); - var addedPropertyObject = App.ServiceConfigProperty.create(addedProperty); - stepConfigs.get('configs').pushObject(addedPropertyObject); - addedPropertyObject.validate(); - } else { - if (cp) { - var newOverride = App.config.createOverride(cp, coreObject, selectedGroup); - selectedGroup.get('properties').pushObject(newOverride); - } else { - stepConfigs.get('configs').push(App.config.createCustomGroupConfig(name, fileName, recommendedValue, selectedGroup, true, true)); - } - } - }, - - /** - * @param configProperty - * @param popupProperty - * @param serviceName - * @param recommendedValue - * @param updateInitial - * @returns {*} - * @private - */ - _getInitialValue: function(configProperty, popupProperty, serviceName, recommendedValue, updateInitial) { - if (!this.useInitialValue(serviceName)) { - return configProperty ? Em.get(configProperty, 'savedValue') : null; - } else if (updateInitial) { - return recommendedValue; - } else { - return popupProperty ? popupProperty.value : configProperty ? Em.get(configProperty, 'initialValue') : null; - } - }, - - /** - * format value for float values - * @param value - * @returns {*} - */ - getFormattedValue: function(value) { - return validator.isValidFloat(value) ? parseFloat(value).toString() : value; - }, - - /** - * just get config override - * @param cp - * @param selectedGroup - * @returns {*|Object} - * @private - */ - _getOverride: function(cp, selectedGroup) { - return Em.get(cp, 'overrides.length') && Em.get(cp, 'overrides').findProperty('group.name', Em.get(selectedGroup, 'name')); - }, - - /** - * get property form popup - * @param name - * @param fileName - * @param groupName - * @returns {Object} - */ - getPopupProperty: function(name, fileName, groupName) { - return this.get('_dependentConfigValues').find(function (dcv) { - return dcv.propertyName === name && dcv.fileName === App.config.getOriginalFileName(fileName) && dcv.configGroup === (groupName || "Default"); - }); - }, - - /** - * add or update proeprty in popup - * @param popupProperty - * @param name - * @param fileName - * @param recommendedValue - * @param initialValue - * @param service - * @param groupName - * @param parentPropertiesNames - * @private - */ - _updatePopup: function(popupProperty, name, fileName, recommendedValue, initialValue, service, groupName, parentPropertiesNames) { - if (popupProperty) { - Em.set(popupProperty, 'recommendedValue', recommendedValue); - Em.set(popupProperty, 'isDeleted', Em.isNone(recommendedValue)); - } else { - var popupPropertyObject = { - saveRecommended: true, - saveRecommendedDefault: true, - fileName: App.config.getOriginalFileName(fileName), - propertyName: name, - - isDeleted: Em.isNone(recommendedValue), - notDefined: Em.isNone(initialValue), - - configGroup: groupName, - value: initialValue, - parentConfigs: parentPropertiesNames, - serviceName: service.get('serviceName'), - allowChangeGroup: groupName!= "Default" && (service.get('serviceName') != this.get('selectedService.serviceName')) - && (App.ServiceConfigGroup.find().filterProperty('serviceName', service.get('serviceName')).length > 1), - serviceDisplayName: service.get('displayName'), - recommendedValue: recommendedValue - }; - this.get('_dependentConfigValues').pushObject(popupPropertyObject); - } - }, - - /** - * clean properties that have same current and recommended values - * @private - */ - _cleanUpPopupProperties: function() { - var cleanDependentList = this.get('_dependentConfigValues').filter(function(d) { - return !((Em.isNone(d.value) && Em.isNone(d.recommendedValue)) || d.value == d.recommendedValue); - }, this); - this.set('_dependentConfigValues', cleanDependentList); - }, - - /** * Helper method to get property from the <code>stepConfigs</code> * * @param {String} name - config property name http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/mixins/common/serverValidator.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/serverValidator.js b/ambari-web/app/mixins/common/serverValidator.js index 7938729..40dd054 100644 --- a/ambari-web/app/mixins/common/serverValidator.js +++ b/ambari-web/app/mixins/common/serverValidator.js @@ -174,7 +174,7 @@ App.ServerValidatorMixin = Em.Mixin.create({ * @param data */ loadRecommendationsSuccess: function(data) { - this._saveRecommendedValues(data, false, null, false, true); + this._saveRecommendedValues(data, null, false); this.set("recommendationsConfigs", Em.get(data.resources[0] , "recommendations.blueprint.configurations")); this.set('recommendationTimeStamp', (new Date).getTime()); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/models/configs/objects/service_config.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/configs/objects/service_config.js b/ambari-web/app/models/configs/objects/service_config.js index 2bc151b..56b8aa4 100644 --- a/ambari-web/app/models/configs/objects/service_config.js +++ b/ambari-web/app/models/configs/objects/service_config.js @@ -59,6 +59,10 @@ App.ServiceConfig = Ember.Object.extend({ }, this); }.observes('configsWithErrors'), + configTypes: function() { + return App.StackService.find(this.get('serviceName')).get('configTypeList') || []; + }.property('serviceName'), + observeForeignKeys: function() { //TODO refactor or move this logic to other place var configs = this.get('configs'); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/models/configs/objects/service_config_property.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/models/configs/objects/service_config_property.js b/ambari-web/app/models/configs/objects/service_config_property.js index 3a4ca3d..4b48a98 100644 --- a/ambari-web/app/models/configs/objects/service_config_property.js +++ b/ambari-web/app/models/configs/objects/service_config_property.js @@ -506,6 +506,20 @@ App.ServiceConfigProperty = Em.Object.extend({ }.property('displayType', 'name', 'App.isHadoop22Stack'), /** + * Get override for selected group + * + * @param {String} groupName + * @returns {App.ServiceConfigProperty|null} + */ + getOverride: function(groupName) { + Em.assert('Group name should be defined string', (typeof groupName === 'string') && groupName); + if (this.get('overrides.length')) { + return this.get('overrides').findProperty('group.name', groupName); + } + return null; + }, + + /** * Update description for `password`-config * Add extra-message about their comparison * http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/routes/add_service_routes.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/routes/add_service_routes.js b/ambari-web/app/routes/add_service_routes.js index 35b06ab..30bdcbc 100644 --- a/ambari-web/app/routes/add_service_routes.js +++ b/ambari-web/app/routes/add_service_routes.js @@ -199,7 +199,7 @@ module.exports = App.WizardRoute.extend({ recommendationsConfigs: null }); router.get('wizardStep7Controller').set('recommendationsConfigs', null); - router.get('wizardStep7Controller').clearDependentConfigs(); + router.get('wizardStep7Controller').clearAllRecommendations(); router.transitionTo('step4'); }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs index ca62592..cef7be2 100644 --- a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs +++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs @@ -44,12 +44,12 @@ {{config.configGroup}} </a></span> </td> - <td class="config-dependency-filename">{{config.fileName}}</td> + <td class="config-dependency-filename">{{config.propertyFileName}}</td> <td class="config-dependency-value"> {{#if config.notDefined}} <i>{{t popup.dependent.configs.table.not.defined}}</i> {{else}} - {{config.value}} + {{config.initialValue}} {{/if}} </td> <td class="config-dependency-recommended-value"> http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/utils/config.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js index f4c8034..a533994 100644 --- a/ambari-web/app/utils/config.js +++ b/ambari-web/app/utils/config.js @@ -19,6 +19,7 @@ var App = require('app'); require('utils/configs_collection'); var stringUtils = require('utils/string_utils'); +var validator = require('utils/validator'); var configTagFromFileNameMap = {}; @@ -212,7 +213,7 @@ App.config = Em.Object.create({ */ getServiceByConfigType: function(configType) { return App.StackService.find().find(function(s) { - return Object.keys(s.get('configTypes')).contains(configType); + return s.get('configTypeList').contains(configType); }); }, @@ -495,6 +496,29 @@ App.config = Em.Object.create({ }, /** + * Format float value + * + * @param {*} value + * @returns {string|*} + */ + formatValue: function(value) { + return validator.isValidFloat(value) ? parseFloat(value).toString() : value; + }, + + /** + * Get step config by file name + * + * @param stepConfigs + * @param fileName + * @returns {Object|null} + */ + getStepConfigForProperty: function (stepConfigs, fileName) { + return stepConfigs.find(function (s) { + return s.get('configTypes').contains(App.config.getConfigTagFromFileName(fileName)); + }); + }, + + /** * * @param configs * @returns {Object[]} http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/views/common/controls_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/controls_view.js b/ambari-web/app/views/common/controls_view.js index a88be76..367a7ee 100644 --- a/ambari-web/app/views/common/controls_view.js +++ b/ambari-web/app/views/common/controls_view.js @@ -113,9 +113,9 @@ App.SupportsDependentConfigs = Ember.Mixin.create({ */ restoreDependentConfigs: function(parentConfig) { var controller = this.get('controller'); - var dependentConfigs = controller.get('_dependentConfigValues'); + var dependentConfigs = controller.get('recommendations'); try { - controller.set('_dependentConfigValues', dependentConfigs.reject(function(item) { + controller.set('recommendations', dependentConfigs.reject(function(item) { if (item.parentConfigs.contains(parentConfig.get('name'))) { if (item.parentConfigs.length > 1) { item.parentConfigs.removeObject(parentConfig.get('name')); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/app/views/common/modal_popups/select_groups_popup.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/modal_popups/select_groups_popup.js b/ambari-web/app/views/common/modal_popups/select_groups_popup.js index 264d7b1..33c72a2 100644 --- a/ambari-web/app/views/common/modal_popups/select_groups_popup.js +++ b/ambari-web/app/views/common/modal_popups/select_groups_popup.js @@ -64,7 +64,7 @@ App.showSelectGroupsPopup = function (selectedServiceName, selectedConfigGroup, var currentGroupName = this.get('selectedGroups')[serviceName] || ""; var configGroup = this.get('dependentStepConfigs').findProperty('serviceName', serviceName).get('configGroups').findProperty('name', selectedGroupName); if (selectedGroupName != currentGroupName) { - /** changing config group for _dependentConfigValues **/ + /** changing config group for recommendations **/ configs.filterProperty('serviceName', serviceName).filterProperty('configGroup', selectedGroupName).forEach(function (c) { if (configs.filterProperty('serviceName', serviceName).filterProperty('configGroup', currentGroupName)) { configs.removeObject(c); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/test/controllers/main/service/info/config_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/service/info/config_test.js b/ambari-web/test/controllers/main/service/info/config_test.js index b6abb8e..ba8f1d3 100644 --- a/ambari-web/test/controllers/main/service/info/config_test.js +++ b/ambari-web/test/controllers/main/service/info/config_test.js @@ -376,9 +376,9 @@ describe("App.MainServiceInfoConfigsController", function () { it("should clear dependent configs", function() { mainServiceInfoConfigsController.set('groupsToSave', { HDFS: 'my cool group'}); - mainServiceInfoConfigsController.set('_dependentConfigValues', Em.A([{name: 'prop_1'}])); + mainServiceInfoConfigsController.set('recommendations', Em.A([{name: 'prop_1'}])); mainServiceInfoConfigsController.doCancel(); - expect(App.isEmptyObject(mainServiceInfoConfigsController.get('_dependentConfigValues'))).to.be.true; + expect(App.isEmptyObject(mainServiceInfoConfigsController.get('recommendations'))).to.be.true; }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/test/mixins/common/configs/enhanced_configs_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js index 1b7115c..3ec4b07 100644 --- a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js +++ b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js @@ -24,19 +24,21 @@ describe('App.EnhancedConfigsMixin', function() { var instanceObject = mixinObject.create({}); describe('#removeCurrentFromDependentList()', function() { it('update some fields', function() { - instanceObject.get('_dependentConfigValues').pushObject({ + instanceObject.get('recommendations').pushObject({ saveRecommended: true, saveRecommendedDefault: true, + configGroup: "Default", propertyName: 'p1', - fileName: 'f1', + propertyFileName: 'f1', value: 'v1' }); instanceObject.removeCurrentFromDependentList(Em.Object.create({name: 'p1', filename: 'f1.xml', value: 'v2'})); - expect(instanceObject.get('_dependentConfigValues')[0]).to.eql({ + expect(instanceObject.get('recommendations')[0]).to.eql({ saveRecommended: false, saveRecommendedDefault: false, + configGroup: "Default", propertyName: 'p1', - fileName: 'f1', + propertyFileName: 'f1', value: 'v1' }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/73a850eb/ambari-web/test/views/common/configs/widgets/config_widget_view_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/common/configs/widgets/config_widget_view_test.js b/ambari-web/test/views/common/configs/widgets/config_widget_view_test.js index e7f979b..ed43a1b 100644 --- a/ambari-web/test/views/common/configs/widgets/config_widget_view_test.js +++ b/ambari-web/test/views/common/configs/widgets/config_widget_view_test.js @@ -209,22 +209,22 @@ describe('App.ConfigWidgetView', function () { tests.forEach(function(test) { it(test.m, function() { - view.set('controller._dependentConfigValues', test.dependentConfigs); + view.set('controller.recommendations', test.dependentConfigs); view.restoreDependentConfigs(view.get('config')); - expect(view.get('controller._dependentConfigValues').mapProperty('name')).to.be.eql(test.e); + expect(view.get('controller.recommendations').mapProperty('name')).to.be.eql(test.e); }); }); it('when dependent configs has multiple parents appropriate parent config should be removed', function() { - view.set('controller._dependentConfigValues', [ + view.set('controller.recommendations', [ {name: 'dependent1', parentConfigs: ['config1', 'config2']}, {name: 'dependent2', parentConfigs: ['config2', 'config1']}, {name: 'dependent3', parentConfigs: ['config1']} ]); view.restoreDependentConfigs(view.get('config')); - expect(view.get('controller._dependentConfigValues').findProperty('name', 'dependent1').parentConfigs.toArray()).to.be.eql(["config2"]); - expect(view.get('controller._dependentConfigValues').findProperty('name', 'dependent2').parentConfigs.toArray()).to.be.eql(["config2"]); - expect(view.get('controller._dependentConfigValues.length')).to.be.eql(2); + expect(view.get('controller.recommendations').findProperty('name', 'dependent1').parentConfigs.toArray()).to.be.eql(["config2"]); + expect(view.get('controller.recommendations').findProperty('name', 'dependent2').parentConfigs.toArray()).to.be.eql(["config2"]); + expect(view.get('controller.recommendations.length')).to.be.eql(2); }); it('dependent config value should be set with inital or saved when it has one parent', function() { @@ -237,7 +237,7 @@ describe('App.ConfigWidgetView', function () { ]) }) ]); - view.set('controller._dependentConfigValues', [ + view.set('controller.recommendations', [ {propertyName: 'dependent1', parentConfigs: ['config1', 'config2'], fileName: 'some-file' }, {propertyName: 'dependent2', parentConfigs: ['config2', 'config1'], fileName: 'some-file'}, {propertyName: 'dependent3', parentConfigs: ['config1'], fileName: 'some-file' }
