Repository: ambari Updated Branches: refs/heads/trunk d09dd4ecc -> df3ab63c9
http://git-wip-us.apache.org/repos/asf/ambari/blob/df3ab63c/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 new file mode 100644 index 0000000..b32d816 --- /dev/null +++ b/ambari-web/app/mixins/common/configs/configs_saver.js @@ -0,0 +1,1323 @@ +/** + * 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 dataManipulationUtils = require('utils/data_manipulation'); +var lazyLoading = require('utils/lazy_loading'); + +App.ConfigsSaverMixin = Em.Mixin.create({ + + /** + * @type {boolean} + */ + saveConfigsFlag: true, + /** + * tells controller in saving configs was started + * for now just changes flag <code>saveInProgress<code> to true + * @private + * @method startSave + */ + startSave: function() { + this.set("saveInProgress", true); + }, + + /** + * tells controller that save has been finished + * for now just changes flag <code>saveInProgress<code> to true + * @private + * @method completeSave + */ + completeSave: function() { + this.set("saveInProgress", false); + }, + + + /** + * show some warning popups before user save configs + * @method showWarningPopupsBeforeSave + * @private + * @method showWarningPopupsBeforeSave + */ + showWarningPopupsBeforeSave: function() { + var self = this; + if (this.isDirChanged()) { + App.showConfirmationPopup(function() { + self.showChangedDependentConfigs(null, function() { + self.restartServicePopup(); + }); + }, + Em.I18n.t('services.service.config.confirmDirectoryChange').format(self.get('content.displayName')), + this.completeSave.bind(this) + ); + } else { + self.showChangedDependentConfigs(null, function() { + self.restartServicePopup(); + }); + } + }, + + /** + * Runs config validation before save + * @private + * @method restartServicePopup + */ + restartServicePopup: function () { + this.serverSideValidation() + .done(this.saveConfigs.bind(this)) + .fail(this.completeSave.bind(this)); + }, + + /** + * Define if user has changed some dir properties + * @return {Boolean} + * @private + * @method isDirChanged + */ + isDirChanged: function () { + var dirChanged = false; + var serviceName = this.get('content.serviceName'); + + if (serviceName === 'HDFS') { + var hdfsConfigs = this.get('stepConfigs').findProperty('serviceName', 'HDFS').get('configs'); + if ((hdfsConfigs.findProperty('name', 'dfs.namenode.name.dir') && hdfsConfigs.findProperty('name', 'dfs.namenode.name.dir').get('isNotDefaultValue')) || + (hdfsConfigs.findProperty('name', 'dfs.namenode.checkpoint.dir') && hdfsConfigs.findProperty('name', 'dfs.namenode.checkpoint.dir').get('isNotDefaultValue')) || + (hdfsConfigs.findProperty('name', 'dfs.datanode.data.dir') && hdfsConfigs.findProperty('name', 'dfs.datanode.data.dir').get('isNotDefaultValue'))) { + dirChanged = true; + } + } + return dirChanged; + }, + + /** + * get config properties for that fileNames that was changed + * @param stepConfigs + * @returns {Array} + */ + getModifiedConfigs: function(stepConfigs) { + var modifiedConfigs = stepConfigs + // get only modified and created configs + .filter(function (config) { + return config.get('isNotDefaultValue') || config.get('isNotSaved'); + }) + // get file names and add file names that was modified, for example after property removing + .mapProperty('filename').concat(this.get('modifiedFileNames')).uniq() + // get configs by filename + .map(function (fileName) { + return stepConfigs.filterProperty('filename', fileName); + }); + + if (!!modifiedConfigs.length) { + // concatenate results + modifiedConfigs = modifiedConfigs.reduce(function (current, prev) { + return current.concat(prev); + }); + } + return modifiedConfigs; + }, + + /** + * Save changed configs and config groups + * @method saveConfigs + */ + saveConfigs: function () { + var selectedConfigGroup = this.get('selectedConfigGroup'); + var configs = this.get('stepConfigs').findProperty('serviceName', this.get('content.serviceName')).get('configs'); + + if (selectedConfigGroup.get('isDefault')) { + if (this.get('content.serviceName') === 'YARN') { + configs = App.config.textareaIntoFileConfigs(configs, 'capacity-scheduler.xml'); + } + + /** + * generates list of properties that was changed + * @type {Array} + */ + var modifiedConfigs = this.getModifiedConfigs(configs); + + // save modified original configs that have no group + this.saveSiteConfigs(modifiedConfigs.filter(function (config) { + return !config.get('group'); + })); + + /** + * First we put cluster configurations, which automatically creates /configurations + * resources. Next we update host level overrides. + */ + this.doPUTClusterConfigurations(); + + } else { + + var overridenConfigs = this.getConfigsForGroup(configs, selectedConfigGroup.get('name')); + + /** + * if there are some changes in dependent configs + * need to save these config to in separate request + */ + this.get('dependentServiceNames').forEach(function(serviceName) { + var serviceConfig = this.get('stepConfigs').findProperty('serviceName', serviceName); + if (serviceConfig && this.get('changedProperties').findProperty('serviceName', serviceName) && this.get('groupsToSave')[serviceName]) { + var stepConfigs = serviceConfig.get('configs'); + + if (this.get('groupsToSave')[serviceName].contains('Default')) { + var data = []; + + var modifiedConfigs = this.getModifiedConfigs(stepConfigs); + + var fileNamesToSave = modifiedConfigs.mapProperty('filename').uniq(); + + var dependentConfigsToSave = this.generateDesiredConfigsJSON(modifiedConfigs, fileNamesToSave, this.get('serviceConfigNote')); + + if (dependentConfigsToSave.length > 0) { + data.pushObject(JSON.stringify({ + Clusters: { + desired_config: dependentConfigsToSave + } + })); + } + this.doPUTClusterConfigurationSites(data, false); + } else { + var overridenConfigs = this.getConfigsForGroup(stepConfigs, selectedConfigGroup.get('name')); + var group = this.get('dependentConfigGroups').findProperty('name', this.get('groupsToSave')[serviceName]); + this.saveGroup(overridenConfigs, group, false); + } + } + }, this); + + this.saveGroup(overridenConfigs, selectedConfigGroup, true); + } + }, + + /** + * get configs that belongs to config group + * @param stepConfigs + * @param configGroupName + */ + getConfigsForGroup: function(stepConfigs, configGroupName) { + var overridenConfigs = []; + + stepConfigs.filterProperty('isOverridden', true).forEach(function (config) { + overridenConfigs = overridenConfigs.concat(config.get('overrides')); + }); + // find custom original properties that assigned to selected config group + overridenConfigs = overridenConfigs.concat(stepConfigs.filterProperty('group') + .filter(function (config) { + return config.get('group.name') == configGroupName; + })); + + this.formatConfigValues(overridenConfigs); + return overridenConfigs; + }, + + /** + * save config group + * @param overridenConfigs + * @param selectedConfigGroup + * @param showPopup + */ + saveGroup: function(overridenConfigs, selectedConfigGroup, showPopup) { + var groupHosts = []; + var fileNamesToSave = overridenConfigs.mapProperty('filename'); + selectedConfigGroup.get('hosts').forEach(function (hostName) { + groupHosts.push({"host_name": hostName}); + }); + this.putConfigGroupChanges({ + ConfigGroup: { + "id": selectedConfigGroup.get('id'), + "cluster_name": App.get('clusterName'), + "group_name": selectedConfigGroup.get('name'), + "tag": selectedConfigGroup.get('service.id'), + "description": selectedConfigGroup.get('description'), + "hosts": groupHosts, + "service_config_version_note": this.get('serviceConfigVersionNote'), + "desired_configs": this.generateDesiredConfigsJSON(overridenConfigs, fileNamesToSave, null, true) + } + }, showPopup); + }, + /** + * On save configs handler. Open save configs popup with appropriate message + * @private + * @method onDoPUTClusterConfigurations + */ + onDoPUTClusterConfigurations: function () { + var header, message, messageClass, value, status = 'unknown', urlParams = '', + result = { + flag: this.get('saveConfigsFlag'), + message: null, + value: null + }, + extendedModel = App.Service.extendedModel[this.get('content.serviceName')], + currentService = extendedModel ? App[extendedModel].find(this.get('content.serviceName')) : App.Service.find(this.get('content.serviceName')); + + if (!result.flag) { + result.message = Em.I18n.t('services.service.config.failSaveConfig'); + } + + App.router.get('clusterController').updateClusterData(); + App.router.get('updateController').updateComponentConfig(function () { + }); + var flag = result.flag; + if (result.flag === true) { + header = Em.I18n.t('services.service.config.saved'); + message = Em.I18n.t('services.service.config.saved.message'); + messageClass = 'alert alert-success'; + // warn the user if any of the components are in UNKNOWN state + urlParams += ',ServiceComponentInfo/installed_count,ServiceComponentInfo/total_count'; + if (this.get('content.serviceName') === 'HDFS') { + urlParams += '&ServiceComponentInfo/service_name.in(HDFS)' + } + } else { + header = Em.I18n.t('common.failure'); + message = result.message; + messageClass = 'alert alert-error'; + value = result.value; + } + if(currentService){ + App.QuickViewLinks.proto().set('content', currentService); + App.QuickViewLinks.proto().loadTags(); + } + this.showSaveConfigsPopup(header, flag, message, messageClass, value, status, urlParams); + }, + + /** + * Show save configs popup + * @return {App.ModalPopup} + * @private + * @method showSaveConfigsPopup + */ + showSaveConfigsPopup: function (header, flag, message, messageClass, value, status, urlParams) { + var self = this; + if (flag) { + this.set('forceTransition', flag); + self.loadStep(); + } + return App.ModalPopup.show({ + header: header, + primary: Em.I18n.t('ok'), + secondary: null, + onPrimary: function () { + this.hide(); + if (!flag) { + self.completeSave(); + } + }, + onClose: function () { + this.hide(); + self.completeSave(); + }, + disablePrimary: true, + bodyClass: Ember.View.extend({ + flag: flag, + message: function () { + return this.get('isLoaded') ? message : Em.I18n.t('services.service.config.saving.message'); + }.property('isLoaded'), + messageClass: function () { + return this.get('isLoaded') ? messageClass : 'alert alert-info'; + }.property('isLoaded'), + setDisablePrimary: function () { + this.get('parentView').set('disablePrimary', !this.get('isLoaded')); + }.observes('isLoaded'), + runningHosts: [], + runningComponentCount: 0, + unknownHosts: [], + unknownComponentCount: 0, + siteProperties: value, + isLoaded: false, + componentsFilterSuccessCallback: function (response) { + var count = 0, + view = this, + lazyLoadHosts = function (dest) { + lazyLoading.run({ + initSize: 20, + chunkSize: 50, + delay: 50, + destination: dest, + source: hosts, + context: view + }); + }, + /** + * Map components for their hosts + * Return format: + * <code> + * { + * host1: [component1, component2, ...], + * host2: [component3, component4, ...] + * } + * </code> + * @return {object} + */ + setComponents = function (item, components) { + item.host_components.forEach(function (c) { + var name = c.HostRoles.host_name; + if (!components[name]) { + components[name] = []; + } + components[name].push(App.format.role(item.ServiceComponentInfo.component_name)); + }); + return components; + }, + /** + * Map result of <code>setComponents</code> to array + * @return {{name: string, components: string}[]} + */ + setHosts = function (components) { + var hosts = []; + Em.keys(components).forEach(function (key) { + hosts.push({ + name: key, + components: components[key].join(', ') + }); + }); + return hosts; + }, + components = {}, + hosts = []; + switch (status) { + case 'unknown': + response.items.filter(function (item) { + return (item.ServiceComponentInfo.total_count > item.ServiceComponentInfo.started_count + item.ServiceComponentInfo.installed_count); + }).forEach(function (item) { + var total = item.ServiceComponentInfo.total_count, + started = item.ServiceComponentInfo.started_count, + installed = item.ServiceComponentInfo.installed_count, + unknown = total - started + installed; + components = setComponents(item, components); + count += unknown; + }); + hosts = setHosts(components); + this.set('unknownComponentCount', count); + lazyLoadHosts(this.get('unknownHosts')); + break; + case 'started': + response.items.filterProperty('ServiceComponentInfo.started_count').forEach(function (item) { + var started = item.ServiceComponentInfo.started_count; + components = setComponents(item, components); + count += started; + hosts = setHosts(components); + }); + this.set('runningComponentCount', count); + lazyLoadHosts(this.get('runningHosts')); + break; + } + }, + componentsFilterErrorCallback: function () { + this.set('isLoaded', true); + }, + didInsertElement: function () { + return App.ajax.send({ + name: 'components.filter_by_status', + sender: this, + data: { + clusterName: App.get('clusterName'), + urlParams: urlParams + }, + success: 'componentsFilterSuccessCallback', + error: 'componentsFilterErrorCallback' + }); + }, + getDisplayMessage: function () { + var displayMsg = []; + var siteProperties = this.get('siteProperties'); + if (siteProperties) { + siteProperties.forEach(function (_siteProperty) { + var displayProperty = _siteProperty.siteProperty; + var displayNames = _siteProperty.displayNames; + + if (displayNames && displayNames.length) { + if (displayNames.length === 1) { + displayMsg.push(displayProperty + Em.I18n.t('as') + displayNames[0]); + } else { + var name; + displayNames.forEach(function (_name, index) { + if (index === 0) { + name = _name; + } else if (index === siteProperties.length - 1) { + name = name + Em.I18n.t('and') + _name; + } else { + name = name + ', ' + _name; + } + }, this); + displayMsg.push(displayProperty + Em.I18n.t('as') + name); + + } + } else { + displayMsg.push(displayProperty); + } + }, this); + } + return displayMsg; + + }.property('siteProperties'), + + runningHostsMessage: function () { + return Em.I18n.t('services.service.config.stopService.runningHostComponents').format(this.get('runningComponentCount'), this.get('runningHosts.length')); + }.property('runningComponentCount', 'runningHosts.length'), + + unknownHostsMessage: function () { + return Em.I18n.t('services.service.config.stopService.unknownHostComponents').format(this.get('unknownComponentCount'), this.get('unknownHosts.length')); + }.property('unknownComponentCount', 'unknownHosts.length'), + + templateName: require('templates/main/service/info/configs_save_popup') + }) + }) + }, + + /** + * construct desired_configs for config groups from overriden properties + * @param configs + * @param timeTag + * @return {Array} + * @private + * @method buildGroupDesiredConfigs + */ + buildGroupDesiredConfigs: function (configs, timeTag) { + var sites = []; + var time = timeTag || (new Date).getTime(); + var siteFileNames = configs.mapProperty('filename').uniq(); + sites = siteFileNames.map(function (filename) { + return { + type: filename.replace('.xml', ''), + tag: 'version' + time, + properties: [] + }; + }); + + configs.forEach(function (config) { + var type = config.get('filename').replace('.xml', ''); + var site = sites.findProperty('type', type); + site.properties.push(config); + }); + + return sites.map(function (site) { + return this.createSiteObj(site.type, site.tag, site.properties); + }, this); + }, + /** + * persist properties of config groups to server + * show result popup if <code>showPopup</code> is true + * @param data {Object} + * @param showPopup {Boolean} + * @method putConfigGroupChanges + */ + putConfigGroupChanges: function (data, showPopup) { + var ajaxOptions = { + name: 'config_groups.update_config_group', + sender: this, + data: { + id: data.ConfigGroup.id, + configGroup: data + } + }; + if (showPopup) { + ajaxOptions.success = "putConfigGroupChangesSuccess"; + } + return App.ajax.send(ajaxOptions); + }, + + /** + * @private + * @method putConfigGroupChangesSuccess + */ + putConfigGroupChangesSuccess: function () { + this.set('saveConfigsFlag', true); + this.onDoPUTClusterConfigurations(); + }, + + /** + * set hive hostnames in configs + * @param configs + * @private + * @method setHiveHostName + */ + setHiveHostName: function (configs) { + var dbHostPropertyName = null, configsToRemove = []; + if (configs.someProperty('name', 'hive_database')) { + var hiveDb = configs.findProperty('name', 'hive_database'); + + switch(hiveDb.value) { + case 'New MySQL Database': + case 'New PostgreSQL Database': + dbHostPropertyName = configs.someProperty('name', 'hive_ambari_host') ? 'hive_ambari_host' : dbHostPropertyName; + configsToRemove = ['hive_existing_mysql_host', 'hive_existing_mysql_database', 'hive_existing_oracle_host', 'hive_existing_oracle_database', 'hive_existing_postgresql_host', 'hive_existing_postgresql_database', 'hive_existing_mssql_server_database', 'hive_existing_mssql_server_host', 'hive_existing_mssql_server_2_database', 'hive_existing_mssql_server_2_host']; + break; + case 'Existing MySQL Database': + dbHostPropertyName = configs.someProperty('name', 'hive_existing_mysql_host') ? 'hive_existing_mysql_host' : dbHostPropertyName; + configsToRemove = ['hive_ambari_database', 'hive_existing_oracle_host', 'hive_existing_oracle_database', 'hive_existing_postgresql_host', 'hive_existing_postgresql_database', 'hive_existing_mssql_server_database', 'hive_existing_mssql_server_host', 'hive_existing_mssql_server_2_database', 'hive_existing_mssql_server_2_host']; + break; + case 'Existing PostgreSQL Database': + dbHostPropertyName = configs.someProperty('name', 'hive_existing_postgresql_host') ? 'hive_existing_postgresql_host' : dbHostPropertyName; + configsToRemove = ['hive_ambari_database', 'hive_existing_mysql_host', 'hive_existing_mysql_database', 'hive_existing_oracle_host', 'hive_existing_oracle_database', 'hive_existing_mssql_server_database', 'hive_existing_mssql_server_host', 'hive_existing_mssql_server_2_database', 'hive_existing_mssql_server_2_host']; + break; + case 'Existing Oracle Database': + dbHostPropertyName = configs.someProperty('name', 'hive_existing_oracle_host') ? 'hive_existing_oracle_host' : dbHostPropertyName; + configsToRemove = ['hive_ambari_database', 'hive_existing_mysql_host', 'hive_existing_mysql_database', 'hive_existing_postgresql_host', 'hive_existing_postgresql_database', 'hive_existing_mssql_server_database', 'hive_existing_mssql_server_host', 'hive_existing_mssql_server_2_database', 'hive_existing_mssql_server_2_host']; + break; + case 'Existing MSSQL Server database with SQL authentication': + dbHostPropertyName = configs.someProperty('name', 'hive_existing_mssql_server_host') ? 'hive_existing_mssql_server_host' : dbHostPropertyName; + configsToRemove = ['hive_ambari_database', 'hive_existing_mysql_host', 'hive_existing_mysql_database', 'hive_existing_postgresql_host', 'hive_existing_postgresql_database', 'hive_existing_oracle_host', 'hive_existing_oracle_database', 'hive_existing_mssql_server_2_database', 'hive_existing_mssql_server_2_host']; + break; + case 'Existing MSSQL Server database with integrated authentication': + dbHostPropertyName = configs.someProperty('name', 'hive_existing_mssql_server_2_host') ? 'hive_existing_mssql_server_2_host' : dbHostPropertyName; + configsToRemove = ['hive_ambari_database', 'hive_existing_mysql_host', 'hive_existing_mysql_database', 'hive_existing_postgresql_host', 'hive_existing_postgresql_database', 'hive_existing_oracle_host', 'hive_existing_oracle_database', 'hive_existing_mssql_server_database', 'hive_existing_mssql_server_host']; + break; + } + configs = dataManipulationUtils.rejectPropertyValues(configs, 'name', configsToRemove); + } + if (dbHostPropertyName) { + var hiveHostNameProperty = App.ServiceConfigProperty.create(App.config.get('preDefinedSiteProperties').findProperty('name', 'hive_hostname')); + hiveHostNameProperty.set('value', configs.findProperty('name', dbHostPropertyName).get('value')); + configs.pushObject(hiveHostNameProperty); + } + return configs; + }, + + /** + * set oozie hostnames in configs + * @param configs + * @private + * @method setOozieHostName + */ + setOozieHostName: function (configs) { + var dbHostPropertyName = null, configsToRemove = []; + if (configs.someProperty('name', 'oozie_database')) { + var oozieDb = configs.findProperty('name', 'oozie_database'); + switch (oozieDb.value) { + case 'New Derby Database': + configsToRemove = ['oozie_ambari_database', 'oozie_existing_mysql_host', 'oozie_existing_mysql_database', 'oozie_existing_oracle_host', 'oozie_existing_oracle_database', 'oozie_existing_postgresql_host', 'oozie_existing_postgresql_database', 'oozie_existing_mssql_server_database', 'oozie_existing_mssql_server_host', 'oozie_existing_mssql_server_2_database', 'oozie_existing_mssql_server_2_host']; + break; + case 'New MySQL Database': + var ambariHost = configs.findProperty('name', 'oozie_ambari_host'); + if (ambariHost) { + ambariHost.name = 'oozie_hostname'; + } + configsToRemove = ['oozie_existing_mysql_host', 'oozie_existing_mysql_database', 'oozie_existing_oracle_host', 'oozie_existing_oracle_database', 'oozie_derby_database', 'oozie_existing_postgresql_host', 'oozie_existing_postgresql_database', 'oozie_existing_mssql_server_database', 'oozie_existing_mssql_server_host', 'oozie_existing_mssql_server_2_database', 'oozie_existing_mssql_server_2_host']; + break; + case 'Existing MySQL Database': + dbHostPropertyName = configs.someProperty('name', 'oozie_existing_mysql_host') ? 'oozie_existing_mysql_host' : dbHostPropertyName; + configsToRemove = ['oozie_ambari_database', 'oozie_existing_oracle_host', 'oozie_existing_oracle_database', 'oozie_derby_database', 'oozie_existing_postgresql_host', 'oozie_existing_postgresql_database', 'oozie_existing_mssql_server_database', 'oozie_existing_mssql_server_host', 'oozie_existing_mssql_server_2_database', 'oozie_existing_mssql_server_2_host']; + break; + case 'Existing PostgreSQL Database': + dbHostPropertyName = configs.someProperty('name', 'oozie_existing_postgresql_host') ? 'oozie_existing_postgresql_host' : dbHostPropertyName; + configsToRemove = ['oozie_ambari_database', 'oozie_existing_mysql_host', 'oozie_existing_mysql_database', 'oozie_existing_oracle_host', 'oozie_existing_oracle_database', 'oozie_existing_mssql_server_database', 'oozie_existing_mssql_server_host', 'oozie_existing_mssql_server_2_database', 'oozie_existing_mssql_server_2_host']; + break; + case 'Existing Oracle Database': + dbHostPropertyName = configs.someProperty('name', 'oozie_existing_oracle_host') ? 'oozie_existing_oracle_host' : dbHostPropertyName; + configsToRemove = ['oozie_ambari_database', 'oozie_existing_mysql_host', 'oozie_existing_mysql_database', 'oozie_derby_database', 'oozie_existing_mssql_server_database', 'oozie_existing_mssql_server_host', 'oozie_existing_mssql_server_2_database', 'oozie_existing_mssql_server_2_host']; + break; + case 'Existing MSSQL Server database with SQL authentication': + dbHostPropertyName = configs.someProperty('name', 'oozie_existing_mssql_server_host') ? 'oozie_existing_mssql_server_host' : dbHostPropertyName; + configsToRemove = ['oozie_ambari_database', 'oozie_existing_oracle_host', 'oozie_existing_oracle_database', 'oozie_derby_database', 'oozie_existing_postgresql_host', 'oozie_existing_postgresql_database', 'oozie_existing_mysql_host', 'oozie_existing_mysql_database', 'oozie_existing_mssql_server_2_database', 'oozie_existing_mssql_server_2_host']; + break; + case 'Existing MSSQL Server database with integrated authentication': + dbHostPropertyName = configs.someProperty('name', 'oozie_existing_mssql_server_2_host') ? 'oozie_existing_mssql_server_2_host' : dbHostPropertyName; + configsToRemove = ['oozie_ambari_database', 'oozie_existing_oracle_host', 'oozie_existing_oracle_database', 'oozie_derby_database', 'oozie_existing_postgresql_host', 'oozie_existing_postgresql_database', 'oozie_existing_mysql_host', 'oozie_existing_mysql_database', 'oozie_existing_mssql_server_database', 'oozie_existing_mssql_server_host']; + break; + } + configs = dataManipulationUtils.rejectPropertyValues(configs, 'name', configsToRemove); + } + + if (dbHostPropertyName) { + var oozieHostNameProperty = App.ServiceConfigProperty.create(App.config.get('preDefinedSiteProperties').findProperty('name', 'oozie_hostname')); + oozieHostNameProperty.set('value', configs.findProperty('name', dbHostPropertyName).get('value')); + configs.pushObject(oozieHostNameProperty); + } + return configs; + }, + + /** + * save site configs + * @param configs + * @private + * @method saveSiteConfigs + */ + saveSiteConfigs: function (configs) { + //storedConfigs contains custom configs as well + configs = this.setHiveHostName(configs); + configs = this.setOozieHostName(configs); + this.formatConfigValues(configs); + var mappedConfigs = App.config.excludeUnsupportedConfigs(this.get('configMapping').all(), App.Service.find().mapProperty('serviceName')); + var allUiConfigs = this.loadUiSideConfigs(mappedConfigs); + this.set('uiConfigs', configs.concat(allUiConfigs)); + }, + + /** + * Reprecent boolean value as string (true => 'true', false => 'false') and trim other values + * @param serviceConfigProperties + * @private + * @method formatConfigValues + */ + formatConfigValues: function (serviceConfigProperties) { + serviceConfigProperties.forEach(function (_config) { + if (typeof _config.get('value') === "boolean") _config.set('value', _config.value.toString()); + _config.set('value', App.config.trimProperty(_config, true)); + }); + }, + + /** + * return configs from the UI side + * @param configMapping array with configs + * @return {Array} + * @private + * @method loadUiSideConfigs + */ + loadUiSideConfigs: function (configMapping) { + var uiConfig = []; + var configs = configMapping.filterProperty('foreignKey', null); + this.addDynamicProperties(configs); + configs.forEach(function (_config) { + var valueWithOverrides = this.getGlobConfigValueWithOverrides(_config.templateName, _config.value, _config.name); + if (valueWithOverrides !== null) { + uiConfig.pushObject({ + "id": "site property", + "name": _config.name, + "value": valueWithOverrides.value, + "filename": _config.filename, + "overrides": valueWithOverrides.overrides + }); + } + }, this); + return uiConfig; + }, + + /** + * @param configs + * @private + * @method addDynamicProperties + */ + addDynamicProperties: function (configs) { + var allConfigs = this.get('stepConfigs').findProperty('serviceName', this.get('content.serviceName')).get('configs'); + var templetonHiveProperty = allConfigs.someProperty('name', 'templeton.hive.properties'); + if (!templetonHiveProperty && this.get('content.serviceName') === 'HIVE') { + configs.pushObject({ + "name": "templeton.hive.properties", + "templateName": ["hive.metastore.uris"], + "foreignKey": null, + "value": "hive.metastore.local=false,hive.metastore.uris=<templateName[0]>,hive.metastore.sasl.enabled=yes,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse", + "filename": "webhcat-site.xml" + }); + } + }, + + /** + * return config value + * @param templateName + * @param expression + * @param name + * @return {Object} + * example: <code>{ + * value: '...', + * overrides: { + * 'value1': [h1, h2], + * 'value2': [h3] + * } + * }</code> + * @private + * @method getGlobConfigValueWithOverrides + */ + getGlobConfigValueWithOverrides: function (templateName, expression, name) { + var express = expression.match(/<(.*?)>/g); + var value = expression; + var overrideHostToValue = {}; + if (express != null) { + express.forEach(function (_express) { + var index = parseInt(_express.match(/\[([\d]*)(?=\])/)[1]); + var globalObj = this.get('allConfigs').findProperty('name', templateName[index]); + if (globalObj) { + var globOverride = globalObj.overrides; + if (globOverride != null) { + for (var ov in globOverride) { + globOverride[ov].forEach(function (host) { + var replacedVal = (host in overrideHostToValue) ? overrideHostToValue[host] : expression; + overrideHostToValue[host] = App.config.replaceConfigValues(name, _express, replacedVal, ov); + }, this); + } + } + value = App.config.replaceConfigValues(name, _express, expression, globalObj.value); + } else { + value = null; + } + }, this); + } + return this.getValueWithOverrides(value, overrideHostToValue) + }, + + /** + * @param value + * @param overrideHostToValue + * @returns {{value: *, overrides: {}}} + * @private + * @method getValueWithOverrides + */ + getValueWithOverrides: function (value, overrideHostToValue) { + var valueWithOverrides = { + value: value, + overrides: {} + }; + if (!jQuery.isEmptyObject(overrideHostToValue)) { + for (var host in overrideHostToValue) { + var hostVal = overrideHostToValue[host]; + if (!(hostVal in valueWithOverrides.overrides)) { + valueWithOverrides.overrides[hostVal] = []; + } + valueWithOverrides.overrides[hostVal].push(host); + } + } + return valueWithOverrides; + }, + + /** + * Saves cluster level configurations for all necessary sites + * PUT calls are made to /api/v1/clusters/clusterName for each site + * @private + * @method doPUTClusterConfigurations + */ + doPUTClusterConfigurations: function () { + this.set('saveConfigsFlag', true); + var serviceConfigTags = this.get('serviceConfigTags'); + /** + * adding config tags for dependentConfigs + */ + for (var i = 0; i < this.get('dependentFileNames.length'); i++) { + if (!serviceConfigTags.findProperty('siteName', this.get('dependentFileNames')[i])) { + serviceConfigTags.pushObject({siteName: this.get('dependentFileNames')[i]}); + } + } + this.setNewTagNames(serviceConfigTags); + var siteNameToServerDataMap = {}; + var configsToSave = []; + serviceConfigTags.forEach(function (_serviceTags) { + var configs = this.createConfigObject(_serviceTags.siteName, _serviceTags.newTagName); + if (configs) { + configsToSave.push(configs); + siteNameToServerDataMap[_serviceTags.siteName] = configs; + } + }, this); + configsToSave = this.filterChangedConfiguration(configsToSave); + if (configsToSave.length > 0) { + var data = []; + data.pushObject(JSON.stringify({ + Clusters: { + desired_config: configsToSave + } + })); + if (App.get('supports.enhancedConfigs')) { + /** + * adding configs that were changed for dependent services + * if there are such configs + */ + this.get('dependentServiceNames').forEach(function(serviceName) { + + var serviceConfigs = this.get('stepConfigs').findProperty('serviceName', serviceName).get('configs'); + + var modifiedConfigs = this.getModifiedConfigs(serviceConfigs); + + var fileNamesToSave = modifiedConfigs.mapProperty('filename').uniq(); + + var dependentConfigsToSave = this.generateDesiredConfigsJSON(modifiedConfigs, fileNamesToSave, this.get('serviceConfigNote')); + if (dependentConfigsToSave.length > 0) { + data.pushObject(JSON.stringify({ + Clusters: { + desired_config: dependentConfigsToSave + } + })); + } + }, this); + } + this.doPUTClusterConfigurationSites(data, true); + } else { + this.onDoPUTClusterConfigurations(); + } + }, + + /** + * create different config object depending on siteName + * @param {String} siteName + * @param {String} tagName + * @returns {Object|null} + * @private + * @method createConfigObject + */ + createConfigObject: function (siteName, tagName) { + console.log("TRACE: Inside " + siteName); + var configObject = {}; + switch (siteName) { + case 'core-site': + if (this.get('content.serviceName') === 'HDFS' || this.get('content.serviceName') === 'GLUSTERFS') { + configObject = this.createCoreSiteObj(tagName); + } else { + return null; + } + break; + default: + var filename = App.config.getOriginalFileName(siteName); + if (filename === 'mapred-queue-acls.xml') { + return null; + } + configObject = this.createSiteObj(siteName, tagName, this.get('uiConfigs').filterProperty('filename', filename)); + break; + } + configObject.service_config_version_note = this.get('serviceConfigVersionNote'); + return configObject; + }, + + /** + * filter out unchanged configurations + * @param {Array} configsToSave + * @private + * @method filterChangedConfiguration + */ + filterChangedConfiguration: function (configsToSave) { + var changedConfigs = []; + + configsToSave.forEach(function (configSite) { + var oldConfig = App.router.get('configurationController').getConfigsByTags([ + {siteName: configSite.type, tagName: this.loadedClusterSiteToTagMap[configSite.type]} + ]); + oldConfig = oldConfig[0] || {}; + var oldProperties = oldConfig.properties || {}; + var oldAttributes = oldConfig["properties_attributes"] || {}; + var newProperties = configSite.properties || {}; + var newAttributes = configSite["properties_attributes"] || {}; + if (this.isAttributesChanged(oldAttributes, newAttributes) || this.isConfigChanged(oldProperties, newProperties) || this.get('modifiedFileNames').contains(App.config.getOriginalFileName(configSite.type))) { + changedConfigs.push(configSite); + } + }, this); + return changedConfigs; + }, + + /** + * Compares the loaded config values with the saving config values. + * @param {Object} loadedConfig - + * loadedConfig: { + * configName1: "configValue1", + * configName2: "configValue2" + * } + * @param {Object} savingConfig + * savingConfig: { + * configName1: "configValue1", + * configName2: "configValue2" + * } + * @returns {boolean} + * @private + * @method isConfigChanged + */ + isConfigChanged: function (loadedConfig, savingConfig) { + if (loadedConfig != null && savingConfig != null) { + var seenLoadKeys = []; + for (var loadKey in loadedConfig) { + if (!loadedConfig.hasOwnProperty(loadKey)) continue; + seenLoadKeys.push(loadKey); + var loadValue = loadedConfig[loadKey]; + var saveValue = savingConfig[loadKey]; + if ("boolean" == typeof(saveValue)) { + saveValue = saveValue.toString(); + } + if (saveValue == null) { + saveValue = "null"; + } + if (loadValue !== saveValue) { + return true; + } + } + for (var saveKey in savingConfig) { + if (seenLoadKeys.indexOf(saveKey) < 0) { + return true; + } + } + } + return false; + }, + + /** + * Compares the loaded config properties attributes with the saving config properties attributes. + * @param {Object} oldAttributes - + * oldAttributes: { + * supports: { + * final: { + * "configValue1" : "true", + * "configValue2" : "true" + * } + * } + * } + * @param {Object} newAttributes + * newAttributes: { + * supports: { + * final: { + * "configValue1" : "true", + * "configValue2" : "true" + * } + * } + * } + * @returns {boolean} + * @private + * @method isAttributesChanged + */ + isAttributesChanged: function (oldAttributes, newAttributes) { + oldAttributes = oldAttributes.final || {}; + newAttributes = newAttributes.final || {}; + + var key; + for (key in oldAttributes) { + if (oldAttributes.hasOwnProperty(key) + && (!newAttributes.hasOwnProperty(key) || newAttributes[key] !== oldAttributes[key])) { + return true; + } + } + for (key in newAttributes) { + if (newAttributes.hasOwnProperty(key) + && (!oldAttributes.hasOwnProperty(key) || newAttributes[key] !== oldAttributes[key])) { + return true; + } + } + return false; + }, + + /** + * Saves configuration of set of sites. The provided data + * contains the site name and tag to be used. + * @param {Object[]} services + * @param {boolean} showPopup + * @return {$.ajax} + * @method doPUTClusterConfigurationSites + */ + doPUTClusterConfigurationSites: function (services, showPopup) { + var ajaxData = { + name: 'common.across.services.configurations', + sender: this, + data: { + data: '[' + services.toString() + ']' + }, + error: 'doPUTClusterConfigurationSiteErrorCallback' + }; + if (showPopup) { + ajaxData.success = 'doPUTClusterConfigurationSiteSuccessCallback' + } + return App.ajax.send(ajaxData); + }, + + /** + * @private + * @method doPUTClusterConfigurationSiteSuccessCallback + */ + doPUTClusterConfigurationSiteSuccessCallback: function () { + this.onDoPUTClusterConfigurations(); + }, + + /** + * @private + * @method doPUTClusterConfigurationSiteErrorCallback + */ + doPUTClusterConfigurationSiteErrorCallback: function () { + this.set('saveConfigsFlag', false); + this.doPUTClusterConfigurationSiteSuccessCallback(); + }, + + /** + * add newTagName property to each config in serviceConfigs + * @param serviceConfigs + * @private + * @method setNewTagNames + */ + setNewTagNames: function (serviceConfigs) { + var time = (new Date).getTime(); + serviceConfigs.forEach(function (_serviceConfigs) { + _serviceConfigs.newTagName = 'version' + time; + }, this); + }, + + /** + * Save "final" attribute for properties + * @param {Array} properties - array of properties + * @returns {Object|null} + * @method getConfigAttributes + */ + getConfigAttributes: function(properties) { + var attributes = { + final: {} + }; + var finalAttributes = attributes.final; + var hasAttributes = false; + properties.forEach(function (property) { + if (property.isRequiredByAgent !== false && property.isFinal) { + hasAttributes = true; + finalAttributes[property.name] = "true"; + } + }); + if (hasAttributes) { + return attributes; + } + return null; + }, + + /** + * create core site object + * @param tagName + * @return {{type: string, tag: string, properties: object}} + * @method createCoreSiteObj + */ + createCoreSiteObj: function (tagName) { + var coreSiteObj = this.get('uiConfigs').filterProperty('filename', 'core-site.xml'); + var coreSiteProperties = {}; + coreSiteObj.forEach(function (_coreSiteObj) { + coreSiteProperties[_coreSiteObj.name] = _coreSiteObj.value; + //this.recordHostOverride(_coreSiteObj, 'core-site', tagName, this); + }, this); + var result = {"type": "core-site", "tag": tagName, "properties": coreSiteProperties}; + var attributes = this.getConfigAttributes(coreSiteObj); + if (attributes) { + result['properties_attributes'] = attributes; + } + return result; + }, + + /** + * create site object + * @param {string} siteName + * @param {string} tagName + * @param {object[]} siteObj + * @return {Object} + * @method createSiteObj + */ + createSiteObj: function (siteName, tagName, siteObj) { + var heapsizeException = this.get('heapsizeException'); + var heapsizeRegExp = this.get('heapsizeRegExp'); + var siteProperties = {}; + siteObj.forEach(function (_siteObj) { + var value = _siteObj.value; + if (_siteObj.isRequiredByAgent == false) return; + // site object name follow the format *permsize/*heapsize and the value NOT ends with "m" + if (heapsizeRegExp.test(_siteObj.name) && !heapsizeException.contains(_siteObj.name) && !(_siteObj.value).endsWith("m")) { + value += "m"; + } + siteProperties[_siteObj.name] = value; + switch (siteName) { + case 'falcon-startup.properties': + case 'falcon-runtime.properties': + case 'pig-properties': + siteProperties[_siteObj.name] = value; + break; + default: + siteProperties[_siteObj.name] = this.setServerConfigValue(_siteObj.name, value); + } + }, this); + var result = {"type": siteName, "tag": tagName, "properties": siteProperties}; + var attributes = this.getConfigAttributes(siteObj); + if (attributes) { + result['properties_attributes'] = attributes; + } + return result; + }, + + /** + * This method will be moved to config's decorators class. + * + * For now, provide handling for special properties that need + * be specified in special format required for server. + * + * @param configName {String} - name of config property + * @param value {*} - value of config property + * + * @return {String} - formatted value + * @method setServerConfigValue + */ + setServerConfigValue: function (configName, value) { + switch (configName) { + case 'storm.zookeeper.servers': + if( Object.prototype.toString.call( value ) === '[object Array]' ) { + return JSON.stringify(value).replace(/"/g, "'"); + } else { + return value; + } + break; + default: + return value; + } + }, + + /** + * Are some unsaved changes available + * @returns {boolean} + * @method hasUnsavedChanges + */ + hasUnsavedChanges: function () { + return this.get('hash') != this.getHash(); + }, + + /** + * If some configs are changed and user navigates away or select another config-group, show this popup with propose to save changes + * @param {String} path + * @param {object} callback - callback with action to change configs view(change group or version) + * @return {App.ModalPopup} + * @method showSavePopup + */ + showSavePopup: function (path, callback) { + var self = this; + return App.ModalPopup.show({ + header: Em.I18n.t('common.warning'), + bodyClass: Em.View.extend({ + templateName: require('templates/common/configs/save_configuration'), + showSaveWarning: true, + notesArea: Em.TextArea.extend({ + classNames: ['full-width'], + placeholder: Em.I18n.t('dashboard.configHistory.info-bar.save.popup.placeholder'), + onChangeValue: function() { + this.get('parentView.parentView').set('serviceConfigNote', this.get('value')); + }.observes('value') + }) + }), + footerClass: Ember.View.extend({ + templateName: require('templates/main/service/info/save_popup_footer') + }), + primary: Em.I18n.t('common.save'), + secondary: Em.I18n.t('common.cancel'), + onSave: function () { + self.set('serviceConfigVersionNote', this.get('serviceConfigNote')); + self.saveStepConfigs(); + this.hide(); + }, + onDiscard: function () { + self.set('preSelectedConfigVersion', null); + if (path) { + self.set('forceTransition', true); + App.router.route(path); + } else if (callback) { + // Prevent multiple popups + self.set('hash', self.getHash()); + callback(); + } + this.hide(); + }, + onCancel: function () { + this.hide(); + } + }); + }, + /********************************METHODS THAT GENERATES JSON TO SAVE *****************************************/ + + /** + * generating common JSON object for desired configs + * @param configsToSave + * @param fileNamesToSave + * @param serviceConfigNote + * @param {boolean} [isNotDefaultGroup=false] + * @returns {Array} + */ + generateDesiredConfigsJSON: function(configsToSave, fileNamesToSave, serviceConfigNote, isNotDefaultGroup) { + var desired_config = []; + if (Em.isArray(configsToSave) && Em.isArray(fileNamesToSave) && fileNamesToSave.length && configsToSave.length) { + serviceConfigNote = serviceConfigNote || ""; + var tagVersion = "version" + (new Date).getTime(); + + fileNamesToSave.forEach(function(fName) { + if (this.allowSaveSite(fName)) { + var properties = configsToSave.filterProperty('filename', fName); + var type = App.config.getConfigTagFromFileName(fName); + desired_config.push(this.createDesiredConfig(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup)); + } + }, this); + } + return desired_config; + }, + + /** + * for some file names we have a restriction + * and can't save them, in this this method will return false + * @param fName + * @returns {boolean} + */ + allowSaveSite: function(fName) { + switch (fName) { + case 'mapred-queue-acls.xml': + return false; + case 'core-site.xml': + return ['HDFS', 'GLUSTERFS'].contains(this.get('content.serviceName')); + default : + return true; + } + }, + + /** + * generating common JSON object for desired config + * @param {string} type - file name without '.xml' + * @param {string} tagVersion - version + timestamp + * @param {App.ConfigProperty[]} properties - array of properties from model + * @param {string} serviceConfigNote + * @param {boolean} [isNotDefaultGroup=false] + * @returns {{type: string, tag: string, properties: {}, properties_attributes: {}|undefined, service_config_version_note: string|undefined}} + */ + createDesiredConfig: function(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup) { + Em.assert('type and tagVersion should be defined', type && tagVersion); + var desired_config = { + "type": type, + "tag": tagVersion, + "properties": {} + }; + if (!isNotDefaultGroup) { + desired_config.service_config_version_note = serviceConfigNote || ""; + } + var attributes = { final: {} }; + if (Em.isArray(properties)) { + properties.forEach(function(property) { + + if (property.get('isRequiredByAgent')) { + desired_config.properties[property.get('name')] = this.formatValueBeforeSave(property); + /** + * add is final value + */ + if (property.get('isFinal')) { + attributes.final[property.get('name')] = "true"; + } + } + }, this); + } + + if (Object.keys(attributes.final).length) { + desired_config.properties_attributes = attributes; + } + return desired_config; + }, + + /** + * format value before save performs some changing of values + * according to the rules that includes heapsizeException trimming and some custom rules + * @param {App.ConfigProperty} property + * @returns {string} + */ + formatValueBeforeSave: function(property) { + var name = property.get('name'); + var value = property.get('value'); + //TODO check for core-site + if (this.get('heapsizeRegExp').test(name) && !this.get('heapsizeException').contains(name) && !(value).endsWith("m")) { + return value += "m"; + } + if (typeof property.get('value') === "boolean") { + return property.get('value').toString(); + } + switch (name) { + case 'storm.zookeeper.servers': + if (Object.prototype.toString.call(value) === '[object Array]' ) { + return JSON.stringify(value).replace(/"/g, "'"); + } else { + return value; + } + break; + default: + return App.config.trimProperty(property, true); + } + } +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/df3ab63c/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 948865e..467f31c 100644 --- a/ambari-web/app/mixins/common/configs/enhanced_configs.js +++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js @@ -96,119 +96,6 @@ App.EnhancedConfigsMixin = Em.Mixin.create({ * @type {Object} */ groupsToSave: {}, - /********************************METHODS THAT GENERATES JSON TO SAVE *****************************************/ - - /** - * generating common JSON object for desired configs - * @param configsToSave - * @param fileNamesToSave - * @param serviceConfigNote - * @param {boolean} [isNotDefaultGroup=false] - * @returns {Array} - */ - generateDesiredConfigsJSON: function(configsToSave, fileNamesToSave, serviceConfigNote, isNotDefaultGroup) { - var desired_config = []; - if (Em.isArray(configsToSave) && Em.isArray(fileNamesToSave) && fileNamesToSave.length && configsToSave.length) { - serviceConfigNote = serviceConfigNote || ""; - var tagVersion = "version" + (new Date).getTime(); - - fileNamesToSave.forEach(function(fName) { - if (this.allowSaveSite(fName)) { - var properties = configsToSave.filterProperty('filename', fName); - var type = App.config.getConfigTagFromFileName(fName); - desired_config.push(this.createDesiredConfig(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup)); - } - }, this); - } - return desired_config; - }, - - /** - * for some file names we have a restriction - * and can't save them, in this this method will return false - * @param fName - * @returns {boolean} - */ - allowSaveSite: function(fName) { - switch (fName) { - case 'mapred-queue-acls.xml': - return false; - case 'core-site.xml': - return ['HDFS', 'GLUSTERFS'].contains(this.get('content.serviceName')); - default : - return true; - } - }, - - /** - * generating common JSON object for desired config - * @param {string} type - file name without '.xml' - * @param {string} tagVersion - version + timestamp - * @param {App.ConfigProperty[]} properties - array of properties from model - * @param {string} serviceConfigNote - * @param {boolean} [isNotDefaultGroup=false] - * @returns {{type: string, tag: string, properties: {}, properties_attributes: {}|undefined, service_config_version_note: string|undefined}} - */ - createDesiredConfig: function(type, tagVersion, properties, serviceConfigNote, isNotDefaultGroup) { - Em.assert('type and tagVersion should be defined', type && tagVersion); - var desired_config = { - "type": type, - "tag": tagVersion, - "properties": {} - }; - if (!isNotDefaultGroup) { - desired_config.service_config_version_note = serviceConfigNote || ""; - } - var attributes = { final: {} }; - if (Em.isArray(properties)) { - properties.forEach(function(property) { - - if (property.get('isRequiredByAgent')) { - desired_config.properties[property.get('name')] = this.formatValueBeforeSave(property); - /** - * add is final value - */ - if (property.get('isFinal')) { - attributes.final[property.get('name')] = "true"; - } - } - }, this); - } - - if (Object.keys(attributes.final).length) { - desired_config.properties_attributes = attributes; - } - return desired_config; - }, - - /** - * format value before save performs some changing of values - * according to the rules that includes heapsizeException trimming and some custom rules - * @param {App.ConfigProperty} property - * @returns {string} - */ - formatValueBeforeSave: function(property) { - var name = property.get('name'); - var value = property.get('value'); - //TODO check for core-site - if (this.get('heapsizeRegExp').test(name) && !this.get('heapsizeException').contains(name) && !(value).endsWith("m")) { - return value += "m"; - } - if (typeof property.get('value') === "boolean") { - return property.get('value').toString(); - } - switch (name) { - case 'storm.zookeeper.servers': - if (Object.prototype.toString.call(value) === '[object Array]' ) { - return JSON.stringify(value).replace(/"/g, "'"); - } else { - return value; - } - break; - default: - return App.config.trimProperty(property, true); - } - }, /******************************METHODS THAT WORKS WITH DEPENDENT CONFIGS *************************************/ http://git-wip-us.apache.org/repos/asf/ambari/blob/df3ab63c/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 0e9ef12..36750e9 100644 --- a/ambari-web/test/controllers/main/service/info/config_test.js +++ b/ambari-web/test/controllers/main/service/info/config_test.js @@ -332,45 +332,6 @@ describe("App.MainServiceInfoConfigsController", function () { }); }); - describe("#getCurrentServiceComponents", function () { - var t = Em.Object.create({ - content: Em.Object.create({ - hostComponents: [ - Em.Object.create({ - componentName: "componentName1", - displayName: "displayName1" - }), - Em.Object.create({ - componentName: "componentName2", - displayName: "displayName2" - }) - ] - }), - validComponents: Em.A([ - Em.Object.create({ - componentName: "componentName1", - displayName: "displayName1", - selected: false - }), - Em.Object.create({ - componentName: "componentName2", - displayName: "displayName2", - selected: false - }) - ]) - }); - - beforeEach(function () { - mainServiceInfoConfigsController.set("content", { hostComponents: Em.A([])}); - }); - - it("get current service components", function () { - mainServiceInfoConfigsController.get("content.hostComponents").push(t.content.hostComponents[0]); - var com = mainServiceInfoConfigsController.get("getCurrentServiceComponents"); - expect(com[0]).to.eql(t.validComponents[0]); - }); - }); - describe("#getMasterComponentHostValue", function () { var tests = [ { http://git-wip-us.apache.org/repos/asf/ambari/blob/df3ab63c/ambari-web/test/mixins/common/configs/configs_saver_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/mixins/common/configs/configs_saver_test.js b/ambari-web/test/mixins/common/configs/configs_saver_test.js new file mode 100644 index 0000000..abd5f58 --- /dev/null +++ b/ambari-web/test/mixins/common/configs/configs_saver_test.js @@ -0,0 +1,151 @@ +/** + * 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'); + +describe('App.ConfigsSaverMixin', function() { + var mixinObject = Em.Controller.extend(App.ConfigsSaverMixin, {}); + var instanceObject = mixinObject.create({}); + + describe('#allowSaveSite()', function() { + + beforeEach(function() { + instanceObject.set('content', {}); + }); + + it('returns true by default', function() { + expect(instanceObject.allowSaveSite('some-site')).to.be.true + }); + + it('returns false for mapred-queue-acls.xml', function() { + expect(instanceObject.allowSaveSite('mapred-queue-acls.xml')).to.be.false + }); + + it('returns false for core-site but not proper service', function() { + instanceObject.set('content.serviceName', 'ANY'); + expect(instanceObject.allowSaveSite('core-site.xml')).to.be.false + }); + + it('returns true for core-site and proper service', function() { + instanceObject.set('content.serviceName', 'HDFS'); + expect(instanceObject.allowSaveSite('core-site.xml')).to.be.true + }); + }); + + describe('#createDesiredConfig()', function() { + beforeEach(function() { + sinon.stub(instanceObject, 'formatValueBeforeSave', function(property) { + return property.get('value'); + }) + }); + afterEach(function() { + instanceObject.formatValueBeforeSave.restore(); + }); + + it('generates config wil throw error', function() { + expect(instanceObject.createDesiredConfig.bind(instanceObject)).to.throw(Error, 'assertion failed'); + }); + + it('generates config without properties', function() { + expect(instanceObject.createDesiredConfig('type1', 'version1')).to.eql({ + "type": 'type1', + "tag": 'version1', + "properties": {}, + "service_config_version_note": "" + }) + }); + + it('generates config with properties', function() { + expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isRequiredByAgent: true}), Em.Object.create({name: 'p2', value: 'v2', isRequiredByAgent: true})], "note")).to.eql({ + "type": 'type1', + "tag": 'version1', + "properties": { + "p1": 'v1', + "p2": 'v2' + }, + "service_config_version_note": 'note' + }) + }); + + it('generates config with properties and skip isRequiredByAgent', function() { + expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isRequiredByAgent: true}), Em.Object.create({name: 'p2', value: 'v2', isRequiredByAgent: false})], "note")).to.eql({ + "type": 'type1', + "tag": 'version1', + "properties": { + p1: 'v1' + }, + "service_config_version_note": 'note' + }) + }); + + it('generates config with properties and skip service_config_version_note', function() { + expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isRequiredByAgent: true})], "note", true)).to.eql({ + "type": 'type1', + "tag": 'version1', + "properties": { + p1: 'v1' + } + }) + }); + + it('generates config with final', function() { + expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isFinal: true, isRequiredByAgent: true}), Em.Object.create({name: 'p2', value: 'v2', isRequiredByAgent: true})], "note")).to.eql({ + "type": 'type1', + "tag": 'version1', + "properties": { + p1: 'v1', + p2: 'v2' + }, + "properties_attributes": { + final: { + 'p1': "true" + } + }, + "service_config_version_note": 'note' + }) + }) + }); + + describe('#generateDesiredConfigsJSON()', function() { + beforeEach(function() { + sinon.stub(instanceObject, 'createDesiredConfig', function(type) { + return 'desiredConfig_' + type; + }); + sinon.stub(instanceObject, 'allowSaveSite', function() { + return true; + }); + + }); + afterEach(function() { + instanceObject.createDesiredConfig.restore(); + instanceObject.allowSaveSite.restore(); + }); + + it('generates empty array as data is missing', function() { + expect(instanceObject.generateDesiredConfigsJSON()).to.eql([]); + expect(instanceObject.generateDesiredConfigsJSON(1,1)).to.eql([]); + expect(instanceObject.generateDesiredConfigsJSON([],[])).to.eql([]); + }); + + it('generates array with desired configs', function() { + expect(instanceObject.generateDesiredConfigsJSON([Em.Object.create({'name': 'p1', 'fileName': 'f1.xml'})], ['f1'])).to.eql(['desiredConfig_f1']); + expect(instanceObject.createDesiredConfig).to.be.calledOnce + }) + }); +}); + http://git-wip-us.apache.org/repos/asf/ambari/blob/df3ab63c/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 7cdfb1b..a9cbce7 100644 --- a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js +++ b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js @@ -18,136 +18,5 @@ var App = require('app'); -describe('App.EnhancedConfigsMixin', function() { - - var mixinObject = Em.Controller.extend(App.EnhancedConfigsMixin, {}); - var instanceObject = mixinObject.create({}); - var stackProperty - - describe('#allowSaveSite()', function() { - - beforeEach(function() { - instanceObject.set('content', {}); - }); - - it('returns true by default', function() { - expect(instanceObject.allowSaveSite('some-site')).to.be.true - }); - - it('returns false for mapred-queue-acls.xml', function() { - expect(instanceObject.allowSaveSite('mapred-queue-acls.xml')).to.be.false - }); - - it('returns false for core-site but not proper service', function() { - instanceObject.set('content.serviceName', 'ANY'); - expect(instanceObject.allowSaveSite('core-site.xml')).to.be.false - }); - - it('returns true for core-site and proper service', function() { - instanceObject.set('content.serviceName', 'HDFS'); - expect(instanceObject.allowSaveSite('core-site.xml')).to.be.true - }); - }); - - describe('#createDesiredConfig()', function() { - beforeEach(function() { - sinon.stub(instanceObject, 'formatValueBeforeSave', function(property) { - return property.get('value'); - }) - }); - afterEach(function() { - instanceObject.formatValueBeforeSave.restore(); - }); - - it('generates config wil throw error', function() { - expect(instanceObject.createDesiredConfig.bind(instanceObject)).to.throw(Error, 'assertion failed'); - }); - - it('generates config without properties', function() { - expect(instanceObject.createDesiredConfig('type1', 'version1')).to.eql({ - "type": 'type1', - "tag": 'version1', - "properties": {}, - "service_config_version_note": "" - }) - }); - - it('generates config with properties', function() { - expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isRequiredByAgent: true}), Em.Object.create({name: 'p2', value: 'v2', isRequiredByAgent: true})], "note")).to.eql({ - "type": 'type1', - "tag": 'version1', - "properties": { - "p1": 'v1', - "p2": 'v2' - }, - "service_config_version_note": 'note' - }) - }); - - it('generates config with properties and skip isRequiredByAgent', function() { - expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isRequiredByAgent: true}), Em.Object.create({name: 'p2', value: 'v2', isRequiredByAgent: false})], "note")).to.eql({ - "type": 'type1', - "tag": 'version1', - "properties": { - p1: 'v1' - }, - "service_config_version_note": 'note' - }) - }); - - it('generates config with properties and skip service_config_version_note', function() { - expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isRequiredByAgent: true})], "note", true)).to.eql({ - "type": 'type1', - "tag": 'version1', - "properties": { - p1: 'v1' - } - }) - }); - - it('generates config with final', function() { - expect(instanceObject.createDesiredConfig('type1', 'version1', [Em.Object.create({name: 'p1', value: 'v1', isFinal: true, isRequiredByAgent: true}), Em.Object.create({name: 'p2', value: 'v2', isRequiredByAgent: true})], "note")).to.eql({ - "type": 'type1', - "tag": 'version1', - "properties": { - p1: 'v1', - p2: 'v2' - }, - "properties_attributes": { - final: { - 'p1': "true" - } - }, - "service_config_version_note": 'note' - }) - }) - }); - - describe('#generateDesiredConfigsJSON()', function() { - beforeEach(function() { - sinon.stub(instanceObject, 'createDesiredConfig', function(type) { - return 'desiredConfig_' + type; - }); - sinon.stub(instanceObject, 'allowSaveSite', function() { - return true; - }); - - }); - afterEach(function() { - instanceObject.createDesiredConfig.restore(); - instanceObject.allowSaveSite.restore(); - }); - - it('generates empty array as data is missing', function() { - expect(instanceObject.generateDesiredConfigsJSON()).to.eql([]); - expect(instanceObject.generateDesiredConfigsJSON(1,1)).to.eql([]); - expect(instanceObject.generateDesiredConfigsJSON([],[])).to.eql([]); - }); - - it('generates array with desired configs', function() { - expect(instanceObject.generateDesiredConfigsJSON([Em.Object.create({'name': 'p1', 'fileName': 'f1.xml'})], ['f1'])).to.eql(['desiredConfig_f1']); - expect(instanceObject.createDesiredConfig).to.be.calledOnce - }) - }); -}); +describe('App.EnhancedConfigsMixin', function() {});
