ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ababiic...@apache.org
Subject [1/2] ambari git commit: AMBARI-10361 Move logic for saving configs to separate mixin (service page only). (ababiichuk)
Date Mon, 06 Apr 2015 10:34:08 GMT
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() {});
 


Mime
View raw message