Repository: ambari Updated Branches: refs/heads/trunk 540f4ee9a -> 74d636392
AMBARI-19308 Non-editable properties should be handled for add/delete host component. (ababiichuk) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/74d63639 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/74d63639 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/74d63639 Branch: refs/heads/trunk Commit: 74d636392d4b82f6322af5a62a6dfe680743aaf4 Parents: 540f4ee Author: ababiichuk <ababiic...@hortonworks.com> Authored: Wed Dec 28 20:52:23 2016 +0200 Committer: ababiichuk <ababiic...@hortonworks.com> Committed: Wed Dec 28 23:26:31 2016 +0200 ---------------------------------------------------------------------- ambari-web/app/controllers/main/host/details.js | 297 +++++++++++-------- ambari-web/app/messages.js | 3 + .../modal_popups/dependent_configs_list.hbs | 138 ++++++--- .../host/details/addDeleteComponentPopup.hbs | 8 +- .../dependent_configs_list_popup.js | 8 +- .../test/controllers/main/host/details_test.js | 121 +++++++- 6 files changed, 383 insertions(+), 192 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/74d63639/ambari-web/app/controllers/main/host/details.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js index c38838f..cb4a2ad 100644 --- a/ambari-web/app/controllers/main/host/details.js +++ b/ambari-web/app/controllers/main/host/details.js @@ -151,33 +151,68 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow } ], + /** + * Determines whether adding/deleting host component requires configs changes + * @type {Boolean} + */ + isReconfigureRequired: false, + + /** + * Array of all properties affected by adding/deleting host component + * @type {Array} + */ + allPropertiesToChange: [], + + /** + * Array of editable properties affected by adding/deleting host component + * @type {Array} + */ + recommendedPropertiesToChange: [], + + /** + * Array of non-editable properties affected by adding/deleting host component + * @type {Array} + */ + requiredPropertiesToChange: [], + + /** + * Properties affected by adding/deleting host component, grouped by service, formatted for PUT call + * @type {Array} + */ + groupedPropertiesToChange: [], + + hasPropertiesToChange: Em.computed.or('recommendedPropertiesToChange.length', 'requiredPropertiesToChange.length'), + addDeleteComponentPopupBody: Em.View.extend({ templateName: require('templates/main/host/details/addDeleteComponentPopup'), - isReconfigure: false, commonMessage: '', manualKerberosWarning: App.get('router.mainAdminKerberosController.isManualKerberos') ? Em.I18n.t('hosts.host.manualKerberosWarning') : '', - propertiesToChange: [], lastComponent: false, - lastComponentError: '', - setPopupSize: function () { - this.set('parentView.hasPropertiesToChange', !!this.get('propertiesToChange.length')); - }.observes('propertiesToChange.length') + lastComponentError: '' }), - applyConfigsCustomization: function (configs) { - configs.propertiesToChange.forEach(function (property) { + clearConfigsChanges: function () { + var arrayNames = ['allPropertiesToChange', 'recommendedPropertiesToChange', 'requiredPropertiesToChange', 'groupedPropertiesToChange']; + arrayNames.forEach(function (arrayName) { + this.get(arrayName).clear(); + }, this); + this.set('isReconfigureRequired', false); + }, + + applyConfigsCustomization: function () { + this.get('recommendedPropertiesToChange').forEach(function (property) { var value = property.saveRecommended ? property.recommendedValue : property.initialValue, filename = property.propertyFileName; - if (configs.groups.length) { - var group = configs.groups.find(function (item) { + if (this.get('groupedPropertiesToChange.length')) { + var group = this.get('groupedPropertiesToChange').find(function (item) { return item.properties.hasOwnProperty(filename); }); if (group) { group.properties[filename][property.propertyName] = value; } } - }); + }, this); }, /** @@ -427,7 +462,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow if (componentsMapItem.deletePropertyName) { this.set(componentsMapItem.deletePropertyName, true); } - returnFunc = this.showDeleteComponentPopup(component, true, componentsMapItem.configsCallbackName); + this.set('isReconfigureRequired', true); + returnFunc = this.showDeleteComponentPopup(component, componentsMapItem.configsCallbackName); } else if (componentName === 'JOURNALNODE') { returnFunc = App.showConfirmationPopup(function () { App.router.transitionTo('main.services.manageJournalNode'); @@ -438,35 +474,31 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow return returnFunc; }, - showDeleteComponentPopup: function (component, isReconfigure, callbackName) { + showDeleteComponentPopup: function (component, callbackName) { var self = this, isLastComponent = (this.getTotalComponent(component) === 1), componentName = component.get('componentName'), componentDisplayName = component.get('displayName'), commonMessage = Em.I18n.t('hosts.host.deleteComponent.popup.msg1').format(componentDisplayName); - if (isReconfigure) { - var configs = { - groups: [], - propertiesToChange: [] - }; + if (this.get('isReconfigureRequired')) { this.set('isConfigsLoadingInProgress', true); this.isServiceMetricsLoaded(function () { - self.loadConfigs(callbackName, configs); + self.loadConfigs(callbackName); }); } return App.ModalPopup.show({ header: Em.I18n.t('popup.confirmation.commonHeader'), controller: self, hasPropertiesToChange: false, - classNameBindings: ['hasPropertiesToChange:common-modal-wrapper', 'hasPropertiesToChange:modal-full-width'], + classNameBindings: ['controller.hasPropertiesToChange:common-modal-wrapper', 'controller.hasPropertiesToChange:modal-full-width'], modalDialogClasses: function () { - return this.get('hasPropertiesToChange') ? ['modal-lg'] : []; - }.property('hasPropertiesToChange'), + return this.get('controller.hasPropertiesToChange') ? ['modal-lg'] : []; + }.property('controller.hasPropertiesToChange'), primary: Em.I18n.t('hosts.host.deleteComponent.popup.confirm'), bodyClass: self.get('addDeleteComponentPopupBody').extend({ - isReconfigure: isReconfigure, commonMessage: commonMessage, - propertiesToChange: isReconfigure ? configs.propertiesToChange : [], + recommendedPropertiesToChange: self.get('recommendedPropertiesToChange'), + requiredPropertiesToChange: self.get('requiredPropertiesToChange'), lastComponentError: Em.I18n.t('hosts.host.deleteComponent.popup.warning').format(componentDisplayName), lastComponent: function () { this.set('parentView.isChecked', !isLastComponent); @@ -475,19 +507,28 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow }), isChecked: false, disablePrimary: function () { - return (isReconfigure && this.get('controller.isConfigsLoadingInProgress')) || !this.get('isChecked'); - }.property('controller.isConfigsLoadingInProgress', 'isChecked'), + return (this.get('controller.isReconfigureRequired') && this.get('controller.isConfigsLoadingInProgress')) || !this.get('isChecked'); + }.property('controller.isReconfigureRequired', 'controller.isConfigsLoadingInProgress', 'isChecked'), onPrimary: function () { this._super(); - if (isReconfigure) { - self.applyConfigsCustomization(configs); + if (self.get('isReconfigureRequired')) { + self.applyConfigsCustomization(); } self._doDeleteHostComponent(componentName, function () { - if (isReconfigure) { - self.saveConfigsBatch(configs.groups, componentName); + if (self.get('isReconfigureRequired')) { + self.saveConfigsBatch(self.get('groupedPropertiesToChange'), componentName); + self.clearConfigsChanges(); } self.set('redrawComponents', true); }); + }, + onSecondary: function () { + this._super(); + self.clearConfigsChanges(); + }, + onClose: function () { + this._super(); + self.clearConfigsChanges(); } }); }, @@ -697,7 +738,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow this.set(componentsMapItem.addPropertyName, false); }; } - returnFunc = self.showAddComponentPopup(component, hostName, null, true, componentsMapItem.configsCallbackName, primary); + this.set('isReconfigureRequired', true); + returnFunc = self.showAddComponentPopup(component, hostName, null, componentsMapItem.configsCallbackName, primary); } else if (componentName === 'JOURNALNODE') { returnFunc = App.showConfirmationPopup(function () { App.router.transitionTo('main.services.manageJournalNode'); @@ -710,19 +752,15 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow return returnFunc; }, - showAddComponentPopup: function (component, hostName, primary, isReconfigure, callbackName, primaryOnReconfigure) { + showAddComponentPopup: function (component, hostName, primary, callbackName, primaryOnReconfigure) { var self = this, componentName = component.get('componentName'), componentDisplayName = component.get('displayName'), commonMessage = Em.I18n.t('hosts.host.addComponent.msg').format(componentDisplayName); - if (isReconfigure) { - var configs = { - groups: [], - propertiesToChange: [] - }; + if (this.get('isReconfigureRequired')) { this.set('isConfigsLoadingInProgress', true); this.isServiceMetricsLoaded(function () { - self.loadConfigs(callbackName, configs); + self.loadConfigs(callbackName); }); } return App.ModalPopup.show({ @@ -731,28 +769,33 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow hasPropertiesToChange: false, classNameBindings: ['hasPropertiesToChange:common-modal-wrapper', 'hasPropertiesToChange:modal-full-width'], modalDialogClasses: function () { - return this.get('hasPropertiesToChange') ? ['modal-lg'] : []; - }.property('hasPropertiesToChange'), + return this.get('controller.hasPropertiesToChange') ? ['modal-lg'] : []; + }.property('controller.hasPropertiesToChange'), primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'), bodyClass: self.get('addDeleteComponentPopupBody').extend({ - commonMessage: commonMessage, - isReconfigure: isReconfigure, - propertiesToChange: isReconfigure ? configs.propertiesToChange : [] + commonMessage: commonMessage }), - disablePrimary: function () { - return isReconfigure && this.get('controller.isConfigsLoadingInProgress'); - }.property('controller.isConfigsLoadingInProgress'), + disablePrimary: Em.computed.and('controller.isReconfigureRequired', 'controller.isConfigsLoadingInProgress'), onPrimary: function () { this._super(); - if (isReconfigure) { - self.applyConfigsCustomization(configs); - self.saveConfigsBatch(configs.groups, componentName, hostName); + if (self.get('isReconfigureRequired')) { + self.applyConfigsCustomization(); + self.saveConfigsBatch(self.get('groupedPropertiesToChange'), componentName, hostName); if (primaryOnReconfigure) { primaryOnReconfigure.call(self); } + self.clearConfigsChanges(); } else if (primary) { primary(); } + }, + onSecondary: function () { + this._super(); + self.clearConfigsChanges(); + }, + onClose: function () { + this._super(); + self.clearConfigsChanges(); } }); }, @@ -824,17 +867,14 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * Success callback for Storm load configs request * @param {object} data - * @param {object} opt - * @param {object} params * @method loadStormConfigs */ - loadStormConfigs: function (data, opt, params) { + loadStormConfigs: function (data) { App.ajax.send({ name: 'admin.get.all_configurations', sender: this, data: { - urlParams: '(type=storm-site&tag=' + data.Clusters.desired_configs['storm-site'].tag + ')', - configs: params.configs + urlParams: '(type=storm-site&tag=' + data.Clusters.desired_configs['storm-site'].tag + ')' }, success: 'onLoadStormConfigs' }); @@ -843,10 +883,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * Update zk configs * @param {object} configs - * @param {object} configsForReview * @method updateZkConfigs */ - updateZkConfigs: function (configs, configsForReview) { + updateZkConfigs: function (configs) { var portValue = configs['zoo.cfg'] && Em.get(configs['zoo.cfg'], 'clientPort'); var zkPort = typeof portValue === 'undefined' ? '2181' : portValue; var infraSolrZnode = configs['infra-solr-env'] ? Em.get(configs['infra-solr-env'], 'infra_solr_znode') : '/ambari-solr'; @@ -854,6 +893,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow var hostComponentsTopology = { masterComponentHosts: [] }; + var propertiesToChange = this.get('allPropertiesToChange'); var masterComponents = this.bootstrapHostsMapping('ZOOKEEPER_SERVER'); if (this.get('fromDeleteHost') || this.get('fromDeleteZkServer')) { this.set('fromDeleteHost', false); @@ -886,19 +926,21 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow }; var configProperty = initializer.initialValue(propertyDef, hostComponentsTopology, dependencies); initializer.updateSiteObj(configs[fileName], configProperty); - if (configsForReview && currentValue !== propertyDef.value) { + if (this.get('isReconfigureRequired') && currentValue !== propertyDef.value) { var service = App.config.get('serviceByConfigTypeMap')[fileName]; - configsForReview.propertiesToChange.pushObject({ + propertiesToChange.pushObject({ propertyFileName: fileName, propertyName: propertyName, serviceDisplayName: service && service.get('displayName'), initialValue: currentValue, - recommendedValue: propertyDef.value, - saveRecommended: true + recommendedValue: propertyDef.value }); } - }); - }); + }, this); + }, this); + if (this.get('isReconfigureRequired')) { + this.setConfigsChangesForDisplay(); + } }, /** @@ -923,35 +965,33 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * update and save Storm related configs to server * @param {object} data - * @param {object} opt - * @param {object} params * @method onLoadStormConfigs */ - onLoadStormConfigs: function (data, opt, params) { + onLoadStormConfigs: function (data) { var nimbusHost = this.get('nimbusHost'), stormNimbusHosts = this.getStormNimbusHosts(), configs = {}, - attributes = {}; + attributes = {}, + propertiesToChange = this.get('allPropertiesToChange'); data.items.forEach(function (item) { configs[item.type] = item.properties; attributes[item.type] = item.properties_attributes || {}; }, this); - this.updateZkConfigs(configs, params.configs); + this.updateZkConfigs(configs); var nimbusSeedsInit = configs['storm-site']['nimbus.seeds'], nimbusSeedsRecommended = JSON.stringify(stormNimbusHosts).replace(/"/g, "'"); configs['storm-site']['nimbus.seeds'] = nimbusSeedsRecommended; - if (params.configs && nimbusSeedsInit !== nimbusSeedsRecommended) { + if (this.get('isReconfigureRequired') && nimbusSeedsInit !== nimbusSeedsRecommended) { var service = App.config.get('serviceByConfigTypeMap')['storm-site']; - params.configs.propertiesToChange.pushObject({ + propertiesToChange.pushObject({ propertyFileName: 'storm-site', propertyName: 'nimbus.seeds', serviceDisplayName: service && service.get('displayName'), initialValue: nimbusSeedsInit, - recommendedValue: nimbusSeedsRecommended, - saveRecommended: true + recommendedValue: nimbusSeedsRecommended }); } var groups = [ @@ -964,8 +1004,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow } } ]; - if (params.configs) { - params.configs.groups.pushObjects(groups); + if (this.get('isReconfigureRequired')) { + this.get('groupedPropertiesToChange').pushObjects(groups); + this.setConfigsChangesForDisplay(); } else { this.saveConfigsBatch(groups, 'NIMBUS', nimbusHost); } @@ -974,11 +1015,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * Success callback for load configs request * @param {object} data - * @param {object} opt - * @param {object} params * @method loadWebHCatConfigs */ - loadWebHCatConfigs: function (data, opt, params) { + loadWebHCatConfigs: function (data) { return App.ajax.send({ name: 'admin.get.all_configurations', sender: this, @@ -989,8 +1028,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow '(type=webhcat-site&tag=' + data.Clusters.desired_configs['webhcat-site'].tag + ')', '(type=hive-env&tag=' + data.Clusters.desired_configs['hive-env'].tag + ')', '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')' - ].join('|'), - configs: params.configs + ].join('|') }, success: 'onLoadHiveConfigs' }); @@ -1003,7 +1041,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow * @param {object} params * @method loadHiveConfigs */ - loadHiveConfigs: function (data, opt, params) { + loadHiveConfigs: function (data) { return App.ajax.send({ name: 'admin.get.all_configurations', sender: this, @@ -1013,8 +1051,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow '(type=webhcat-site&tag=' + data.Clusters.desired_configs['webhcat-site'].tag + ')', '(type=hive-env&tag=' + data.Clusters.desired_configs['hive-env'].tag + ')', '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')' - ].join('|'), - configs: params.configs + ].join('|') }, success: 'onLoadHiveConfigs' }); @@ -1045,7 +1082,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow configs[item.type] = item.properties; attributes[item.type] = item.properties_attributes || {}; }, this); - + var propertiesToChange = this.get('allPropertiesToChange'); port = configs['hive-site']['hive.metastore.uris'].match(/:[0-9]{2,4}/); port = port ? port[0].slice(1) : "9083"; @@ -1070,20 +1107,19 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow value: currentValue }; configs[fileName][propertyName] = Em.get(initializer.initialValue(propertyDef, localDB, dependencies), 'value'); - if (params.configs && propertyDef.value !== currentValue) { + if (this.get('isReconfigureRequired') && propertyDef.value !== currentValue) { var service = App.config.get('serviceByConfigTypeMap')[fileName]; - params.configs.propertiesToChange.pushObject({ + propertiesToChange.pushObject({ propertyFileName: fileName, propertyName: propertyName, serviceDisplayName: service && service.get('displayName'), initialValue: currentValue, - recommendedValue: propertyDef.value, - saveRecommended: true + recommendedValue: propertyDef.value }); } - }); + }, this); } - }); + }, this); initializer.cleanup(); @@ -1109,8 +1145,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow } } ]; - if (params.configs) { - params.configs.groups.pushObjects(groups); + if (this.get('isReconfigureRequired')) { + this.get('groupedPropertiesToChange').pushObjects(groups); + this.setConfigsChangesForDisplay(); } else { var args = [groups]; var componentName = this.get('addHiveServer') ? 'HIVE_SERVER' : (hiveMetastoreHost ? 'HIVE_METASTORE' : 'WEBHCAT_SERVER'); @@ -1234,17 +1271,14 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * Success callback for load configs request * @param {object} data - * @param {object} opt - * @param {object} params * @method loadRangerConfigs */ - loadRangerConfigs: function (data, opt, params) { + loadRangerConfigs: function (data) { App.ajax.send({ name: 'admin.get.all_configurations', sender: this, data: { - urlParams: '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')|(type=hdfs-site&tag=' + data.Clusters.desired_configs['hdfs-site'].tag + ')|(type=kms-env&tag=' + data.Clusters.desired_configs['kms-env'].tag + ')', - configs: params.configs + urlParams: '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')|(type=hdfs-site&tag=' + data.Clusters.desired_configs['hdfs-site'].tag + ')|(type=kms-env&tag=' + data.Clusters.desired_configs['kms-env'].tag + ')' }, success: 'onLoadRangerConfigs' }); @@ -1253,11 +1287,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * update and save Hive hive.metastore.uris config to server * @param {object} data - * @param {object} opt - * @param {object} params * @method onLoadRangerConfigs */ - onLoadRangerConfigs: function (data, opt, params) { + onLoadRangerConfigs: function (data) { var properties = [ { type: 'core-site', @@ -1286,13 +1318,14 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow } } ]; + var propertiesToChange = this.get('allPropertiesToChange'); properties.forEach(function (property) { var typeConfigs = data.items.findProperty('type', property.type).properties, currentValue = typeConfigs[property.name]; - if (params.configs && currentValue !== newValue) { + if (this.get('isReconfigureRequired') && currentValue !== newValue) { var service = App.config.get('serviceByConfigTypeMap')[property.type]; - params.configs.propertiesToChange.pushObject({ + propertiesToChange.pushObject({ propertyFileName: property.type, propertyName: property.name, serviceDisplayName: service && service.get('displayName'), @@ -1302,9 +1335,10 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow }); } typeConfigs[property.name] = newValue; - }); - if (params.configs) { - params.configs.groups.pushObjects(groups); + }, this); + if (this.get('isReconfigureRequired')) { + this.get('groupedPropertiesToChange').pushObjects(groups); + this.setConfigsChangesForDisplay(); } else { this.saveConfigsBatch(groups, 'RANGER_KMS_SERVER', hostToInstall); } @@ -1410,20 +1444,12 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow * This is required to make sure that service metrics API determining the HA state of components is loaded * @method loadConfigs */ - loadConfigs: function (callback, configs) { - var self = this; - this.set('isConfigsLoadingInProgress', true); + loadConfigs: function (callback) { App.ajax.send({ name: 'config.tags', sender: this, success: callback ? callback : 'loadConfigsSuccessCallback', - error: 'onLoadConfigsErrorCallback', - callback: function () { - self.set('isConfigsLoadingInProgress', false); - }, - data: { - configs: configs - } + error: 'onLoadConfigsErrorCallback' }); }, @@ -1438,19 +1464,16 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * Success callback for load configs request * @param {object} data - * @param {object} opt - * @param {object} params * @method loadConfigsSuccessCallback */ - loadConfigsSuccessCallback: function (data, opt, params) { + loadConfigsSuccessCallback: function (data) { var urlParams = this.constructConfigUrlParams(data); if (urlParams.length > 0) { App.ajax.send({ name: 'reassign.load_configs', sender: this, data: { - urlParams: urlParams.join('|'), - configs: params.configs + urlParams: urlParams.join('|') }, success: 'saveZkConfigs' }); @@ -1483,11 +1506,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow /** * save new ZooKeeper configs to server * @param {object} data - * @param {object} opt - * @param {object} params * @method saveZkConfigs */ - saveZkConfigs: function (data, opt, params) { + saveZkConfigs: function (data) { var configs = {}; var attributes = {}; data.items.forEach(function (item) { @@ -1495,7 +1516,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow attributes[item.type] = item.properties_attributes || {}; }, this); - this.updateZkConfigs(configs, params.configs); + this.updateZkConfigs(configs); var groups = []; var installedServiceNames = App.Service.find().mapProperty('serviceName'); this.get('zooKeeperRelatedServices').forEach(function (service) { @@ -1511,8 +1532,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow groups.push(group); } }); - if (params.configs) { - params.configs.groups.pushObjects(groups); + if (this.get('isReconfigureRequired')) { + this.get('groupedPropertiesToChange').pushObjects(groups); } else { this.saveConfigsBatch(groups, 'ZOOKEEPER_SERVER'); } @@ -2726,5 +2747,33 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow */ isServiceMetricsLoaded: function(callback) { App.router.get('mainController').isLoading.call(App.router.get('clusterController'), 'isServiceContentFullyLoaded').done(callback); + }, + + setConfigsChangesForDisplayObserver: function () { + if (App.get('router.clusterController.isConfigsPropertiesLoaded')) { + this.get('allPropertiesToChange').forEach(function (property) { + var stackProperty = App.configsCollection.getConfigByName(property.propertyName, property.propertyFileName); + if (stackProperty && (!stackProperty.isEditable || !stackProperty.isReconfigurable)) { + this.get('requiredPropertiesToChange').pushObject(property); + } else { + Em.set(property, 'saveRecommended', true); + this.get('recommendedPropertiesToChange').pushObject(property); + } + }, this); + this.set('isConfigsLoadingInProgress', false); + this.removeObserver('App.router.clusterController.isConfigsPropertiesLoaded', this, 'setConfigsChangesForDisplayObserver'); + } + }, + + setConfigsChangesForDisplay: function () { + if (this.get('allPropertiesToChange.length')) { + if (App.get('router.clusterController.isConfigsPropertiesLoaded')) { + this.setConfigsChangesForDisplayObserver(); + } else { + this.addObserver('App.router.clusterController.isConfigsPropertiesLoaded', this, 'setConfigsChangesForDisplayObserver'); + } + } else { + this.set('isConfigsLoadingInProgress', false); + } } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/74d63639/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index c941411..6296d55 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -465,10 +465,13 @@ Em.I18n.translations = { 'popup.dependent.configs.header': 'Dependent Configurations', 'popup.dependent.configs.title.recommendation': 'Based on your configuration changes, Ambari is recommending the following dependent configuration changes.', 'popup.dependent.configs.title.values': 'Ambari will update all checked configuration changes to the <b>Recommended Value</b>. Uncheck any configuration to retain the <b>Current Value</b>.', + 'popup.dependent.configs.table.recommended': 'Recommended Changes', + 'popup.dependent.configs.table.required': 'Required Changes', 'popup.dependent.configs.table.saveProperty': 'Save property', 'popup.dependent.configs.table.initValue': 'Initial value', 'popup.dependent.configs.table.currentValue': 'Current Value', 'popup.dependent.configs.table.recommendedValue': 'Recommended Value', + 'popup.dependent.configs.table.newValue': 'New Value', 'popup.dependent.configs.table.not.defined': 'Not Defined', http://git-wip-us.apache.org/repos/asf/ambari/blob/74d63639/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs index a1f53ef..af84306 100644 --- a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs +++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs @@ -16,51 +16,97 @@ * limitations under the License. }} -<div class="alert alert-warning"> - {{#if view.isAfterRecommendation}} - <div>{{t popup.dependent.configs.title.recommendation}}</div> - {{/if}} - <div>{{t popup.dependent.configs.title.values}}</div> -</div> +{{#if view.recommendations.length}} + <div class="alert alert-warning"> + {{#if view.isAfterRecommendation}} + <div>{{t popup.dependent.configs.title.recommendation}}</div> + {{/if}} + <div>{{t popup.dependent.configs.title.values}}</div> + </div> +{{/if}} <span id="config-dependencies" class="limited-height-2"> - <table class="table table-hover"> - <thead> - <tr> - <th>{{t common.property}}</th> - <th>{{t common.service}}</th> - <th>{{t common.configGroup}}</th> - <th>{{t common.fileName}}</th> - <th class="row-fluid"> - <div class="col-md-6"> - {{t popup.dependent.configs.table.currentValue}} - </div> - <div class="col-md-6"> - {{t popup.dependent.configs.table.recommendedValue}} - </div> - </th> - <th class="check-box-col">{{view view.toggleAll}}<label {{bindAttr for="view.toggleAllId"}}></label></th> - </tr> - </thead> - <tbody> - {{#each recommendation in view.recommendations}} - <tr {{bindAttr class="recommendation.saveRecommended:active"}}> - <td class="config-dependency-name">{{recommendation.propertyName}}</td> - <td class="config-dependency-service">{{recommendation.serviceDisplayName}}</td> - <td class="config-dependency-group"> - <span {{bindAttr class="recommendation.allowChangeGroup::not-active-link"}} ><a href="javascript:void(null);" class="black" - {{action showSelectGroupPopup recommendation.serviceName target="App.router.mainServiceInfoConfigsController"}}> - {{recommendation.configGroup}} - </a></span> - </td> - <td class="config-dependency-filename">{{recommendation.propertyFileName}}</td> - <td> - <div> - {{view App.ConfigDiffView configBinding="recommendation"}} - </div> - </td> - <td class="check-box-col">{{view App.CheckboxView checkedBinding="recommendation.saveRecommended"}}</td> - </tr> - {{/each}} - </tbody> - </table> + {{#if view.recommendations.length}} + <h4>{{t popup.dependent.configs.table.recommended}}</h4> + <table class="table table-hover"> + <thead> + <tr> + <th>{{t common.property}}</th> + <th>{{t common.service}}</th> + <th>{{t common.configGroup}}</th> + <th>{{t common.fileName}}</th> + <th class="row-fluid"> + <div class="col-md-6"> + {{t popup.dependent.configs.table.currentValue}} + </div> + <div class="col-md-6"> + {{t popup.dependent.configs.table.recommendedValue}} + </div> + </th> + <th class="check-box-col">{{view view.toggleAll}}<label {{bindAttr for="view.toggleAllId"}}></label></th> + </tr> + </thead> + <tbody> + {{#each recommendation in view.recommendations}} + <tr {{bindAttr class="recommendation.saveRecommended:active"}}> + <td class="config-dependency-name">{{recommendation.propertyName}}</td> + <td class="config-dependency-service">{{recommendation.serviceDisplayName}}</td> + <td class="config-dependency-group"> + <span {{bindAttr class="recommendation.allowChangeGroup::not-active-link"}} ><a href="javascript:void(null);" class="black" + {{action showSelectGroupPopup recommendation.serviceName target="App.router.mainServiceInfoConfigsController"}}> + {{recommendation.configGroup}} + </a></span> + </td> + <td class="config-dependency-filename">{{recommendation.propertyFileName}}</td> + <td> + <div> + {{view App.ConfigDiffView configBinding="recommendation"}} + </div> + </td> + <td class="check-box-col">{{view App.CheckboxView checkedBinding="recommendation.saveRecommended"}}</td> + </tr> + {{/each}} + </tbody> + </table> + {{/if}} + {{#if view.requiredChanges.length}} + <h4>{{t popup.dependent.configs.table.required}}</h4> + <table class="table table-hover"> + <thead> + <tr> + <th>{{t common.property}}</th> + <th>{{t common.service}}</th> + <th>{{t common.configGroup}}</th> + <th>{{t common.fileName}}</th> + <th class="row-fluid"> + <div class="col-md-6"> + {{t popup.dependent.configs.table.currentValue}} + </div> + <div class="col-md-6"> + {{t popup.dependent.configs.table.newValue}} + </div> + </th> + </tr> + </thead> + <tbody> + {{#each recommendation in view.requiredChanges}} + <tr> + <td class="config-dependency-name">{{recommendation.propertyName}}</td> + <td class="config-dependency-service">{{recommendation.serviceDisplayName}}</td> + <td class="config-dependency-group"> + <span {{bindAttr class="recommendation.allowChangeGroup::not-active-link"}} ><a href="javascript:void(null);" class="black" + {{action showSelectGroupPopup recommendation.serviceName target="App.router.mainServiceInfoConfigsController"}}> + {{recommendation.configGroup}} + </a></span> + </td> + <td class="config-dependency-filename">{{recommendation.propertyFileName}}</td> + <td> + <div> + {{view App.ConfigDiffView configBinding="recommendation"}} + </div> + </td> + </tr> + {{/each}} + </tbody> + </table> + {{/if}} </span> http://git-wip-us.apache.org/repos/asf/ambari/blob/74d63639/ambari-web/app/templates/main/host/details/addDeleteComponentPopup.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/host/details/addDeleteComponentPopup.hbs b/ambari-web/app/templates/main/host/details/addDeleteComponentPopup.hbs index 295acc8..ed8445e 100644 --- a/ambari-web/app/templates/main/host/details/addDeleteComponentPopup.hbs +++ b/ambari-web/app/templates/main/host/details/addDeleteComponentPopup.hbs @@ -24,17 +24,17 @@ </div> </div> {{/if}} -{{#if view.isReconfigure}} +{{#if controller.isReconfigureRequired}} {{#if controller.isConfigsLoadingInProgress}} {{view App.SpinnerView}} {{else}} {{view.commonMessage}} - {{#if view.propertiesToChange.length}} - {{view App.DependentConfigsListView isAfterRecommendation=false recommendationsBinding="view.propertiesToChange"}} + {{#if controller.hasPropertiesToChange}} + {{view App.DependentConfigsListView isAfterRecommendation=false recommendationsBinding="controller.recommendedPropertiesToChange" requiredChangesBinding="controller.requiredPropertiesToChange"}} {{/if}} {{{view.manualKerberosWarning}}} {{/if}} {{else}} <div>{{view.commonMessage}}</div> <div>{{{view.manualKerberosWarning}}}</div> -{{/if}} \ No newline at end of file +{{/if}} http://git-wip-us.apache.org/repos/asf/ambari/blob/74d63639/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js index 1aa46eb..e8e6df5 100644 --- a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js +++ b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js @@ -22,6 +22,8 @@ App.DependentConfigsListView = Em.View.extend({ templateName: require('templates/common/modal_popups/dependent_configs_list'), isAfterRecommendation: true, recommendations: [], + requiredChanges: [], + toggleAllId: '', toggleAll: App.CheckboxView.extend({ didInsertElement: function () { this.set('parentView.toggleAllId', this.get('elementId')); @@ -32,13 +34,13 @@ App.DependentConfigsListView = Em.View.extend({ }, updateCheckboxObserver: function () { Em.run.once(this, 'updateCheckbox'); - }.observes('parentView.parentView.recommendations.@each.saveRecommended'), + }.observes('parentView.recommendations.@each.saveRecommended'), updateCheckbox: function() { - this.set('checked', !(this.get('parentView.parentView.recommendations') || []).someProperty('saveRecommended', false)); + this.set('checked', !(this.get('parentView.recommendations') || []).someProperty('saveRecommended', false)); }, updateSaveRecommended: function() { - this.get('parentView.parentView.recommendations').setEach('saveRecommended', this.get('checked')); + this.get('parentView.recommendations').setEach('saveRecommended', this.get('checked')); } }) }); http://git-wip-us.apache.org/repos/asf/ambari/blob/74d63639/ambari-web/test/controllers/main/host/details_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js index 024e994..859320e 100644 --- a/ambari-web/test/controllers/main/host/details_test.js +++ b/ambari-web/test/controllers/main/host/details_test.js @@ -592,15 +592,12 @@ describe('App.MainHostDetailsController', function () { tag: 'tag' } } - }}, null, { - configs: {} - }); + }}); var args = testHelpers.findAjaxRequest('name', 'admin.get.all_configurations'); expect(args[0]).exists; expect(args[0].sender).to.be.eql(controller); expect(args[0].data).to.be.eql({ - urlParams: '(type=storm-site&tag=tag)', - configs: {} + urlParams: '(type=storm-site&tag=tag)' }); }); }); @@ -666,15 +663,12 @@ describe('App.MainHostDetailsController', function () { tag: 'tag' } } - }}, null, { - configs: {} - }); + }}); var args = testHelpers.findAjaxRequest('name', 'admin.get.all_configurations'); expect(args[0]).exists; expect(args[0].sender).to.be.eql(controller); expect(args[0].data).to.be.eql({ - urlParams: '(type=hive-site&tag=tag)|(type=webhcat-site&tag=tag)|(type=hive-env&tag=tag)|(type=core-site&tag=tag)', - configs: {} + urlParams: '(type=hive-site&tag=tag)|(type=webhcat-site&tag=tag)|(type=hive-env&tag=tag)|(type=core-site&tag=tag)' }); }); }); @@ -693,15 +687,12 @@ describe('App.MainHostDetailsController', function () { tag: 'tag' } } - }}, null, { - configs: {} - }); + }}); var args = testHelpers.findAjaxRequest('name', 'admin.get.all_configurations'); expect(args[0]).exists; expect(args[0].sender).to.be.eql(controller); expect(args[0].data).to.be.eql({ - urlParams: '(type=core-site&tag=tag)|(type=hdfs-site&tag=tag)|(type=kms-env&tag=tag)', - configs: {} + urlParams: '(type=core-site&tag=tag)|(type=hdfs-site&tag=tag)|(type=kms-env&tag=tag)' }); }); }); @@ -3834,4 +3825,104 @@ describe('App.MainHostDetailsController', function () { }); }); }); + + describe('#setConfigsChangesForDisplayObserver', function () { + + var configsObject, + propertiesToChange = [ + { + propertyName: 'n0', + propertyFileName: 'f0' + }, + { + propertyName: 'n1', + propertyFileName: 'f1' + }, + { + propertyName: 'n2', + propertyFileName: 'f2' + }, + { + propertyName: 'n3', + propertyFileName: 'f3' + } + ], + result = { + recommendedPropertiesToChange: [ + { + propertyName: 'n0', + propertyFileName: 'f0', + saveRecommended: true + }, + { + propertyName: 'n3', + propertyFileName: 'f3', + saveRecommended: true + } + ], + requiredPropertiesToChange: [ + { + propertyName: 'n1', + propertyFileName: 'f1' + }, + { + propertyName: 'n2', + propertyFileName: 'f2' + } + ] + }; + + beforeEach(function () { + controller.setProperties({ + allPropertiesToChange: propertiesToChange, + recommendedPropertiesToChange: [], + requiredPropertiesToChange: [] + }); + sinon.stub(App.configsCollection, 'getConfigByName', function (propertyName) { + var stackProperty; + switch (propertyName) { + case 'n0': + stackProperty = { + isEditable: true, + isReconfigurable: true + }; + break; + case 'n1': + stackProperty = { + isEditable: true, + isReconfigurable: false + }; + break; + case 'n2': + stackProperty = { + isEditable: false, + isReconfigurable: false + }; + break; + } + return stackProperty; + }); + sinon.stub(App, 'get').withArgs('router.clusterController.isConfigsPropertiesLoaded').returns(true); + controller.set('isConfigsLoadingInProgress', true); + controller.setConfigsChangesForDisplayObserver(); + }); + + afterEach(function () { + App.configsCollection.getConfigByName.restore(); + App.get.restore(); + }); + + it('editable changes', function () { + expect(controller.get('recommendedPropertiesToChange').toArray()).to.eql(result.recommendedPropertiesToChange); + }); + + it('non-editable changes', function () { + expect(controller.get('requiredPropertiesToChange').toArray()).to.eql(result.requiredPropertiesToChange); + }); + + it('isConfigsLoadingInProgress', function () { + expect(controller.get('isConfigsLoadingInProgress')).to.be.false; + }); + + }); });